Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Execption innerhalb AnonymousThread behandeln (https://www.delphipraxis.net/182091-execption-innerhalb-anonymousthread-behandeln.html)

Kostas 29. Sep 2014 22:55

Execption innerhalb AnonymousThread behandeln
 
Hallo Zusammen,

ich versuche gerade die Methode GetGruppen in einem AnonymusThread zu packen.
Dabei stellen sich mir eine Menge Frage. Die Methode GetGruppen ist in einem Adroid Mobile Projekt
und ruft über die Methode DataModule1.ServerMethods1Client.GetGruppen ein Dataset aus einem DataSnap Server
und speichert das Ergebnis in ein lokales JASONDataSets Object. Das dürfte im Thread noch keine Probleme machen
das keine Aktionen Objkte auf dem MainThread tangieren.
Die Methode UpdateGruppenNames jedoch packt das JASONDataSets in einer FDMemTable die auf der MainForm liegt.
Mir ist bekannt dass nur der MainThread auf Objekte der Form zugreifen darf. Ist jetzt eine FDMemTable
ebenfalls davon betroffen oder nur Objekte die etwas anzeigen wie z.B.: TLabel, TListView u.s.w.?


Die nächste Frage, was ist mit der Exception innerhalb des Threads, darf ich die ShowMessageFmt werfen
oder muss das in einem TThread.Synchronize erfolgen?


Ohne Thread
Delphi-Quellcode:
procedure TfrMobileMain.GetGruppen;
var
  LDataSetList: TFDJSONDataSets;
begin
  try
    //vom DataSnap Server ein DataSet abrufen
    LDataSetList := DataModule1.ServerMethods1Client.GetGruppen;

    //das DataSet in die MemTable packen
    UpdateGruppenNames(LDataSetList);
  except
    on E: TDSRestProtocolException do
      HandleRestException(DataModule1.DSRestConnection1, 'Get Gruppen error', E);
    on E: Exception do
      ShowMessageFmt('Fehler: %s',[E.Message]);
    else
      raise;
  end;
end;

Mit Thread
Delphi-Quellcode:
procedure TfrMobileMain.GetGruppenThreaded;
begin
  TThread.CreateAnonymousThread(procedure ()
  var
    LDataSetList: TFDJSONDataSets;
  begin
    try

      LDataSetList := DataModule1.ServerMethods1Client.GetGruppen;
      UpdateGruppenNames(LDataSetList);

//      TThread.Synchronize (TThread.CurrentThread,
//        procedure ()
//        begin
//        end);


    except
      on E: TDSRestProtocolException do
        HandleRestException(DataModule1.DSRestConnection1, 'Get Gruppen error', E);
      on E: Exception do
        ShowMessageFmt('Fehler: %s',[E.Message]);
      else
        raise;
    end;

  end).Start;{TThread}

end;
Gruß Kostas

himitsu 30. Sep 2014 04:57

AW: Execption innerhalb AnonymousThread behandeln
 
Eigentlich sollte doch klar sein, wie man das macht?

Zugriffe auf die VCL müssen immer synchronisiert werden, also wieso wird das in der Exceptionbehandlung nicht gemacht?



TThread besitzt standardmäßig keinerlei Exception-
Delphi-Quellcode:
Behandlung
Auswertung.

Eine Behandlung ist drin, da Exceptions, welche bis ins System durchrauschen, den Prozess abschießen.
Exceptions im TThread.Execute werden abgefangen und sind über TThread.FatalException auswertbar, aber das muß man selber machen. (z.B. im TThread.OnTerminate)


Entweder du synchronisierst in deinem Try-Except die VCL-Zugriffe,
oder du läßt dein Try-Except komplett weg und benutzt das Try-Except der RTL, indem du z.B. ein OnTerminate implementierst und da anhängst.
Der Vorteil am OnTerminate ist, daß die RTL diesen Aufruf bereits in den MainThread synchronisiert.


Delphi-Quellcode:
type
  TMyThreadHelper = class abstract
    class procedure DoTerminate(Sender: TObject);
  end;

class procedure TMyThreadHelper.DoTerminate(Sender: TObject);
begin
  if Assigned(TThread(Sender).FatalException) then
    if TThread(Sender).FatalException is TDSRestProtocolException then
      HandleRestException(DataModule1.DSRestConnection1, 'Get Gruppen error', Exception(TThread(Sender).FatalException))
    else
      //ShowMessageFmt('Fehler: %s, [Exception(TThread(Sender).FatalException).Message]);
      //Application.HandleException(TThread(Sender).FatalException);
      ShowException(TThread(Sender).FatalException, nil); // such dir was aus
end;

with TThread.CreateAnonymousThread(
  procedure
  var
    LDataSetList: TFDJSONDataSets;
  begin
    LDataSetList := DataModule1.ServerMethods1Client.GetGruppen;
    UpdateGruppenNames(LDataSetList);
  end) do
begin
  OnTerminate := TMyThreadHelper.DoTerminate;
  Start;{TThread}
end;
Alternativ natürlich nicht ohne die Synchronisierung.
Eventuell auch nur um das ShowMessage, wenn das HandleRestException threadsave ist.
Delphi-Quellcode:
except
  on E: Exception do // alternativ ginge auch ein var E: TObject; und E := ExceptObject;
    TThread.Synchronize(TThread.CurrentThread,
      procedure
      begin
        if E is TDSRestProtocolException then
          HandleRestException(DataModule1.DSRestConnection1, 'Get Gruppen error', Exception(E))
        else //if E is Exception then
          ShowMessageFmt('Fehler: %s', [Exception(E).Message]);
        //else
        //  {nix};
      end);
end;
Ohne Behandlung von FatalException war dein raise etwas umsonst, da es quasi im Nirgendwo verpufft.
Und in Delphi sind (standardmäßig) alle Exceptions vom Typ Exception, so daß es vermutlich eh nicht ausgelöst wird, aber schaden kann der Code auch nichts.

CCRDude 30. Sep 2014 07:22

AW: Execption innerhalb AnonymousThread behandeln
 
Zitat:

Zitat von Kostas (Beitrag 127427)
und ruft über die Methode DataModule1.ServerMethods1Client.GetGruppen ein Dataset aus einem DataSnap Server und speichert das Ergebnis in ein lokales JASONDataSets Object. Das dürfte im Thread noch keine Probleme machen das keine Aktionen Objkte auf dem MainThread tangieren.

Ich kenne jetzt das Framework nicht, aber ist DataModule1 nicht ein Objekt aus dem Mainthread?

Anonyme Threads halte ich für ein Problem in sich - in namentlich deklarierten Threads hat man die räumliche Trennung im Code (eigene Klasse, eigene Datei) und wird gezwungen, sich Gedanken über threadsichere Schnittstellen zu machen.

(Nachtrag: ich habe gerade in einem anderen Thread gelesen, anonyme Funktionen könnten threadsicher auf lokales zugreifen? Dann würde obiges nicht mehr ganz stimmen)

himitsu 30. Sep 2014 07:56

AW: Execption innerhalb AnonymousThread behandeln
 
Bevor Emba sich das mit dem CreateAnonymousThread abgeguckt hat, hatte ich mir schon länger eine "starte (anonyme) Methode als Thread"-Funktion gebaut.

Nur mit einem Unterschied:
* der Thread bekommt einen Namen (NameThreadForDebugging), damit man ihn im Debugger besser erkennt, weiß wo er her kommt und somit schnell sieht was er macht
* und das DoTerminate wurde überschrieben, welches eine Exceptionmeldung in den Hauptthread schiebt (oder alternativ ins Log)

Kostas 30. Sep 2014 08:57

AW: Execption innerhalb AnonymousThread behandeln
 
Zitat:

Zitat von CCRDude (Beitrag 1274285)
Ich kenne jetzt das Framework nicht, aber ist DataModule1 nicht ein Objekt aus dem Mainthread?

(Nachtrag: ich habe gerade in einem anderen Thread gelesen, anonyme Funktionen könnten threadsicher auf lokales zugreifen? Dann würde obiges nicht mehr ganz stimmen)

Eigentlich hast du Recht. Das DataModule1 läuft auf dem Mainthread und beinhaltet die Kommunikation zu DataSnapServer.
Es greift jedoch auf keinerlei visuelle Komponenten zu. Was ich noch nicht verinnerlicht habe, kann ich auf ein TFDMemTable aus einem Thread- anonym oder nicht, sei mal dahingestellt, zugreifen oder muss auch dieser Teil in
einem Synchronize Block.

Delphi-Quellcode:
 TThread.Synchronize (TThread.CurrentThread,
 procedure ()
 begin
   //Zugriff auf die FDMemTable.
 end);

Kostas 30. Sep 2014 08:59

AW: Execption innerhalb AnonymousThread behandeln
 
Herzlichen Danke himitsu,

ich werde deine Methode verwenden mit dem DoTerminate.

CCRDude 30. Sep 2014 09:33

AW: Execption innerhalb AnonymousThread behandeln
 
[QUOTE=Kostas;1274294]
Zitat:

Zitat von CCRDude (Beitrag 1274285)
Das DataModule1 läuft auf dem Mainthread und beinhaltet die Kommunikation zu DataSnapServer.
Es greift jedoch auf keinerlei visuelle Komponenten zu.

Ist das relevant? Klar, bekannt ist "vcl = nicht threadsafe". Aber den Umkehrschluß "nicht vcl = threadsafe" kann man daraus nicht folgern! Im Gegenteil, alles, was nicht explizit threadsicher ist, sollte aus Vorsicht erstmal nicht als solches betrachtet werden. Fehler treten sonst manchmal wirklich sehr willkürlich scheinend auf, manchmal erst viel später.

Der schöne Günther 30. Sep 2014 10:00

AW: Execption innerhalb AnonymousThread behandeln
 
Man muss doch nur schauen was man verwendet- Ein "Datenmodul" ist ja an sich nur eine Gruppierung von mehreren "Komponenten"- Mit VCL, FMX oder sonst was hat das das doch überhaupt nichts mehr zu tun.

Wenn man z.B. ein "Datenmodul" hat das in irgendeiner Datenbank (z.B. mittels FireDAC) wühlen soll, FireDAC meint die Benutzung aus mehreren Threads sei in Ordnung, dann kann ich mein Datenmodul doch auch so benutzen.

Kostas 30. Sep 2014 10:02

AW: Execption innerhalb AnonymousThread behandeln
 
Hallo himitsu,

die Methode HandleRestException benötigt als zweiten Parameter ein String als Fehlermeldung.
Ich habe die DoTerminate erweitert um den Parameter "ErrorMsg". Ob das so geht bin ich mir nicht sicher.
Wenn es so machbar ist, müsste ich beim Aufruf den Sender angeben. Ohne den zusätzlichen
Parameter "ErrorMsg" musste der Sender nicht übergeben werden da es ein TNotifyEvent ist.
Hast du mir bitte noch ein kleinen Hinweis?


Delphi-Quellcode:
class procedure TMyThreadHelper.DoTerminate(ErrorMsg:string; Sender: TObject); <<so geht's nicht
begin
   if Assigned(TThread(Sender).FatalException) then
     if TThread(Sender).FatalException is TDSRestProtocolException then
       HandleRestException(DataModule1.DSRestConnection1, ErrorMsg, TDSRestProtocolException(TThread(Sender).FatalException))
     else
       ShowException(TThread(Sender).FatalException, nil);

end;

Delphi-Quellcode:
OnTerminate := TMyThreadHelper.DoTerminate('Get Gruppen error', ???);

himitsu 30. Sep 2014 10:07

AW: Execption innerhalb AnonymousThread behandeln
 
Beim Überschreiben virtueller Methoden, müssen die Parameter natürlich gleich bleiben.

* Entweder eine böse globale Variable,
* das TThread abgleeitet, aber dann müsste man das CreateAnonymusThread selbst schreiben, um dort seine eigene TThread-Klasse zu erstellen (bei mir ist dort das "Start" auch gleich mit integriert)
* TMyThreadHelper als Instanz mit Feld/Variable und DoTerminate als Methode und nicht als Klassen-Methode
* oder bei eigenen Exceptions kann man ZusatzInfos auch im Exceptions-Objekt mitgeben.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz