Hallo zusammen,
ich habe angefangen, mich mit Threads zu beschäftigen und erste Versuche unternommen, Threads in meine Anwendung einzubauen. Ein Problem, das ich dabei habe, sind Exceptions. In meiner Anwendung verwende ich EurekaLog und möchte, dass der Callstack bei Exceptions im Bugreport enthalten ist. Daher muss ich die
Exception irgendwie nach außen an den aufrufenden bzw. an den Main-Thread weiterleiten.
Ich habe versucht, die
Exception im Thread abzufangen und mit einem Event (OnError) an den Main-Thread weiterzuleiten (TThreadEx ist eine von TThread abgeleitete Klasse von EurekaLog):
Delphi-Quellcode:
TThreadErrorEvent =
procedure(
const E:
Exception)
of object;
TBaseThread =
class(TThreadEx)
strict private
FOnError: TThreadErrorEvent;
procedure DoOnError(
const E:
Exception);
strict protected
procedure Execute;
override;
final;
procedure Run;
virtual;
abstract;
public
constructor Create(CreateSuspended: Boolean);
property OnError: TThreadErrorEvent
read FOnError
write FOnError;
end;
implementation
constructor TBaseThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
end;
procedure TBaseThread.DoOnError(
const E:
Exception);
var
existingException:
Exception;
begin
if (@FOnError =
nil)
OR (E =
nil)
then
Exit;
existingException := AcquireExceptionObject;
Synchronize(
procedure
begin
try
FOnError(existingException);
except
on NewException:
Exception do
begin
if NewException <> existingException
then
E.Free;
// exception has been wrapped or another exception has been raised
raise;
end;
end;
// exception has not been re-raised and not been wrapped
E.Free;
end);
end;
procedure TBaseThread.Execute;
begin
try
inherited;
Run;
except
on E:
Exception do
DoOnError(E);
end;
end;
Mein Problem ist in der Prozedur DoOnError. Ich habe festgestellt, dass ein Memory-Leak entsteht, wenn ich E.Free nicht aufrufe (ReleaseExceptionObject hat nichts gebracht). Den Code finde ich aber ziemlich hässlich und ich könnte mir vorstellen, dass es eine schönere Lösung gibt. Meine Idee ist, in dem Event-Handler FOnError, welcher im Moment im wegen dem Synchronize im Main-Thread ausgeführt wird, die Exceptions zu behandeln, also z. B. einfach weiterzuleiten oder mit
Exception.RaiseOuterException eine neue
Exception herum zu packen oder die "verschwinden" zu lassen (nachdem man entsprechend darauf reagiert hat).
Eine Lösung wäre vllt., das AcquireExceptionObject nicht im DoOnError zu machen sondern jeweils im Event-Handler, das man für FOnError setzt. Das möchte ich jedoch vermeiden, weil ich es sonst jedes Mal neu implementieren muss.
Und noch eine weitere Frage: Ich rufe das Event FOnError durch das Synchronize im Kontext des Main-Threads auf. Gibt es eine Möglichkeit, dies im Kontext eines anderen Threads auszuführen? Wenn ich beispielsweise aus dem Main-Thread einen Thread A starte, welcher wiederum einen Thread B startet, dann möchte ich, dass Thread B einen aufgetretenen Fehler an Thread A schickt und nicht an den Main-Thread.
Ich bin für jede Hilfe dankbar