![]() |
Funktionsweise Thread.WaitFor
Hallo zusammen,
ich habe gerade Probleme mit den Threads. Ich möchte gerne irgendwie warten, bis ein Thread erfolgreich beendet wurde. Dafür habe ich nun die Methode "WaitFor" gefunden. Nur verstehe ich die Funktionsweise nicht. Hier der Aufbau meiner Anwendung:
Delphi-Quellcode:
Es ist klar, dass ich jetzt nur eine Meldung mit "Foo" bekomme, da der Thread ja noch nicht beendet wurde.
unit Unit1;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls, Vcl.ExtCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private-Deklarationen } importList: TStringList; public { Public-Deklarationen } end; TThread1 = class(TThread) protected procedure Execute; override; private IdHTTP1: TIdHTTP; page: String; response: String; end; var Form1: TForm1; implementation {$R *.dfm} procedure TThread1.Execute; begin IdHTTP1 := TIdHTTP.Create(); self.response := IdHTTP1.Get(self.page); end; procedure TForm1.Button1Click(Sender: TObject); var Thread1: TThread1; begin Thread1 := TThread1.Create(True); Thread1.FreeOnTerminate := True; Thread1.response := 'Foo'; Thread1.page := 'http://google.de'; Thread1.Start; showmessage(Thread1.response); end; end. Wie kann ich nun warten, bis der Thread beendet wurde um dann die Meldung auszugeben? |
AW: Funktionsweise Thread.WaitFor
Zitat:
![]() Und wenn du auf den Thread wartest, dann ist deine UI blockiert ... warum dann einen Thread?
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var Thread1: TThread1; begin Thread1 := TThread1.Create(True); try // Thread1.FreeOnTerminate := True; Thread1.response := 'Foo'; Thread1.page := 'http://google.de'; Thread1.Start; Thread1.WaitFor; showmessage(Thread1.response); finally Thread1.Free; end; end; |
AW: Funktionsweise Thread.WaitFor
Ahhhh ok, das war einfach.
Ok, du hast recht, macht natürlich jetzt keinen Sinn, mit nem Thread zu arbeiten. |
AW: Funktionsweise Thread.WaitFor
Du kannst natürlich OnTerminate etwas zuweisen. Das Ereignis wird ausgelöst, wenn der Thread fertig ist
|
AW: Funktionsweise Thread.WaitFor
Und das tolle daran:
Delphi-Quellcode:
wird sogar schon im Hauptthread ausgeführt.
OnTerminate
|
AW: Funktionsweise Thread.WaitFor
Zitat:
Man kann darin sogar auf Exceptions prüfen. Also die, welche im Thread auftrat und wodurch der Thread abgeschossen wurde. Denn, im Gegensatz um Hauptthread, werden diese Exception zwar ebenfalls abgefangen, aber nicht dem Benutzer "angezeigt". Der Grund, warum die RTL/VCL das macht: Windows beendet Prozesse, womit sich das ganze Programm verabschieden würde, wenn in irgendeinem Thread eine Exceptions bis zur Wurzel (ins Windows) durch rauscht. ![]() |
AW: Funktionsweise Thread.WaitFor
Wieder was gelernt, hätte ich das mal früher gewusst 8-)
|
AW: Funktionsweise Thread.WaitFor
Generell sollte man das Thread-Thema etwas anders anfassen:
Man stelle sich das als einen zusätzlichen Mitarbeiter vor, dem man eine Aufgabe gibt und wenn die Aufgabe abgeschlossen ist, dann gibt es wieder eine Rückmeldung. Und wie im wahren Leben hat dieser Mitarbeiter einen Postkorb, wo alle Arbeiten hineinkommen und diese arbeitet der dann der Reihe nach ab. Der Thread
Delphi-Quellcode:
und ein kleine Anwendung
unit HttpRequestThread;
interface uses System.Generics.Collections, System.SyncObjs, System.Classes; type TResponseNotify = procedure( const Request, Response : string ) of object; THttpRequestThread = class( TThread ) private FCS : TCriticalSection; FEvent : TEvent; FQueue : TQueue<string>; FOnResponse : TResponseNotify; procedure SetOnResponse( const Value : TResponseNotify ); function GetOnResponse : TResponseNotify; function GetQueueItem : string; procedure ProcessQueueItem; procedure DoResponseNotify( const ARequest, AResponse : string ); protected procedure Execute; override; procedure TerminatedSet; override; public constructor Create; destructor Destroy; override; procedure Add( const ARequest : string ); property OnResponse : TResponseNotify read GetOnResponse write SetOnResponse; end; implementation uses System.SysUtils, IdException, IdHTTP; { THttpRequestThread } procedure THttpRequestThread.Add( const ARequest : string ); begin FCS.Enter; try FQueue.Enqueue( ARequest ); FEvent.SetEvent; finally FCS.Leave; end; end; constructor THttpRequestThread.Create; begin FCS := TCriticalSection.Create; FEvent := TEvent.Create( nil, False, False, '' ); FQueue := TQueue<string>.Create; inherited Create( False ); end; destructor THttpRequestThread.Destroy; begin inherited; FreeAndNil( FQueue ); FreeAndNil( FEvent ); FreeAndNil( FCS ); end; procedure THttpRequestThread.DoResponseNotify( const ARequest, AResponse : string ); begin if MainThreadID = CurrentThread.ThreadID then begin if Assigned( OnResponse ) then OnResponse( ARequest, AResponse ); end else Queue( procedure begin DoResponseNotify( ARequest, AResponse ); end ); end; procedure THttpRequestThread.Execute; begin inherited; while not Terminated do begin FEvent.WaitFor; if not Terminated then ProcessQueueItem; end; end; function THttpRequestThread.GetOnResponse : TResponseNotify; begin FCS.Enter; try Result := FOnResponse; finally FCS.Leave; end; end; function THttpRequestThread.GetQueueItem : string; begin FCS.Enter; try Result := FQueue.Dequeue; if FQueue.Count > 0 then FEvent.SetEvent; finally FCS.Leave; end; end; procedure THttpRequestThread.ProcessQueueItem; var LRequest : string; LResponse : string; LHttp : TIdHTTP; begin LHttp := TIdHTTP.Create( nil ); LHttp.HandleRedirects := True; try LRequest := GetQueueItem; try LResponse := LHttp.Get( LRequest ); except on E : EIdException do begin LResponse := E.ClassName + ': ' + E.Message; end; end; DoResponseNotify( LRequest, LResponse ); finally LHttp.Free; end; end; procedure THttpRequestThread.SetOnResponse( const Value : TResponseNotify ); begin FCS.Enter; try FOnResponse := Value; finally FCS.Leave; end; end; procedure THttpRequestThread.TerminatedSet; begin inherited; FEvent.SetEvent; end; end.
Delphi-Quellcode:
unit FormMain;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, HttpRequestThread; type TForm1 = class( TForm ) Button1 : TButton; ListBox1 : TListBox; procedure Button1Click( Sender : TObject ); private FHttpRequest : THttpRequestThread; procedure HttpRequestResponse( const Request, Response : string ); procedure LogMsg( const AMsgStr : string ); public procedure AfterConstruction; override; procedure BeforeDestruction; override; end; var Form1 : TForm1; implementation {$R *.dfm} procedure TForm1.AfterConstruction; begin inherited; FHttpRequest := THttpRequestThread.Create; FHttpRequest.OnResponse := HttpRequestResponse; end; procedure TForm1.BeforeDestruction; begin inherited; FreeAndNil( FHttpRequest ); end; procedure TForm1.Button1Click( Sender : TObject ); var LUrl : string; LIdx : Integer; begin LUrl := 'http://google.de'; for LIdx := 1 to 10 do begin LogMsg( 'Request ' + LUrl ); FHttpRequest.Add( LUrl ); end; end; procedure TForm1.HttpRequestResponse( const Request, Response : string ); begin LogMsg( Request + ' => ' + Response ); end; procedure TForm1.LogMsg( const AMsgStr : string ); begin ListBox1.Items.Add( DateTimeToStr( Now ) + ': ' + AMsgStr ); end; end. |
AW: Funktionsweise Thread.WaitFor
Ich hole diesen etwas betagten Thread noch mal hoch, weil:
Prinzipiell selbes Prob, nur hab ich ges. 5 Threads, auf dessen Erledigung ich warte und die weitere Prorammausführung davon abhängt, wer wann fertig ist. Ablauf in etwa so: - Zum Prog-Begin starten 3 Threads um Daten zu lesen & zu evaluieren. (Kann im worst-case bis zu 10 Sekunden dauern !) - Proggie soll derweil weiter initialisieren, die GUI aufbauen, den User grüßen, usw. Warte-Punkt 1) Thread-1 kann, muss aber noch nicht bis dahin fertig sein - d.h. auf den MUSS gewartet werden, weil alles weitere von dessen Daten abhängt. Wenn fertig, erfolgen ab hier weiter GUI-Init's und 2 weitere Threads starten Warte-Punkt 2) Wie unter 1, Thread-2 fertig ? Wobei hier datenabhgig eine Meldgugng angzeigt und entspechend reagiert werden muss. Warte Punkt-3) Das warten auf Thread-3 ist nicht zwingend zeitrelevant (und dauert i.d.R. ach am längsten), es wäre jedoch "nice to have" umgehend nach dessen Ende eine weitere Meldng zu zeigen und das Display mit neuen Daten upzudaten. Meine Idee: Wenn hier jeder Thread seinen eigenen Event nach Ablauf feuert, wie kann ich da in der Main-Form darauf reagieren ? (Hier spez. Thread 1 + 2 in einer Loop) Get das evtl. mit "WaitForSingleObject" ? Für Thread-3 könnte ich auch gut mit einer CallBack-procedure leben. Wichtig ist allgemein, dass das Proggie nicht bei Fehlern oder langen Lesezeiten hängt und weiter bedienbar ist. |
AW: Funktionsweise Thread.WaitFor
Thread 1 2 und 3 kannst du Vermutlich über IFuture abbilden.
Du startest die Futures und machst deinen GUI-Aufbau...bis irgendwo in dem Verlauf die Werte der Futures ausgelesen werden und der GUI-Aufbau wartet dann solange bist die Werte bereittehen. Das warten passiert da wo die Werte der Furures gelesen werden. Für endliche Arbeitslasten nutze ich die Klassen aus AnonThread.pas ![]() Ich brauche diese Klassen um die Oberflächen von Windows und Mobilen Anwendungen Responsive zu halten. Es ist recht komfortabel die Logick die im thread ausgeführt wird in das OnClick Ereignis des Buttons zu inlinen und auch die Erfolgs- oder Misserfolgsmeldung zu inlinen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:39 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz