Autor |
Beitrag |
Bergmann89
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 21.11.10 17:54
Hey,
ich versuch grad einen Thread in meinem Programm mit 2 Buttons anzuhalten und wieder fortzusetzen. Seltsamerweiße bleibt manchmal beim Klick auf einen der beiden Buttons die Form hängen. In den Buttons ruf ich ganz normal Thread.Suspend bzw. Thread.Resume auf. Fehler oder sowas in der Art spuckt er auch nicht aus. Normalerweiße sollte das doch so funktionieren, oder muss ich dem Thread über ne Message sagen, das er anhalten soll?
MfG Bergmann
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
bummi
      
Beiträge: 1248
Erhaltene Danke: 187
XP - Server 2008R2
D2 - Delphi XE
|
Verfasst: So 21.11.10 18:08
CriticalSektions oder Synchronizes in denen Du hängen bleiben könntest?
_________________ Das Problem liegt üblicherweise zwischen den Ohren H₂♂
DRY DRY KISS
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: So 21.11.10 18:12
Je nach Version/OS kannst du die Applikation beim Debuggen pausieren, dann siehst du im Threads-Fenster, welche Threads sich einander gerade blocken und auf welcher Zeile. Hilft dir vielleicht, den Grund zu finden.
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 21.11.10 18:13
Bergmann89 hat folgendes geschrieben : | In den Buttons ruf ich ganz normal Thread.Suspend bzw. Thread.Resume auf. |
Das ist nicht normal, sondern gefährlich...
Denn du weißt nicht wo der Thread anhält. Wenn der da z.B. gerade eine Synchronisation gestartet hat (z.B. via CriticalSection), kannst du in einen Deadlock geraten und nichts geht mehr.
Deshalb (und aus anderen Gründen) sollte man Suspend und Resume niemals von außerhalb des Threads aufrufen. Und am besten lieber mit WaitForMultipleObjects oder ähnlichem das Warten auf die nächste Aktion des Threads realisieren.
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 21.11.10 19:26
Hey,
-CriticalSections hab ich keine drin. Syncronizes hab ich auch nur die, die vom der TThread-Klasse mitgeliefert sind (OnTerminate). Und ab und zu schickt der Thread ne Message an die MainForm, wie weit er fortgeschritten ist.
-Hab mal geguckt, er is jetzt einfach mitten in einer privaten Methode hägen geblieben. Die benutzt auch nur private Felder.
-ich will ja nicht warten, bis der Thread irgendws berechnet hat, sondern ich will ihn bis auf weiteres komplett anhalten. Ich könnte das ja auch so machen wie mit Terminate; einfach eine variable auf True setzten und dann im Thread prüfen und ggf anhalten, dann ist das Suspend; im Thread. Und das Resume; würde ich dann ganz normal beim klick auf den Button aufrufen. Im Thread kann ich das ja nich aufrufen, weil er ja schläft?! Würde das so funktionieren?
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 21.11.10 19:40
Wie gesagt: Nutze die Benachrichtigungen, die Windows beinhaltet:
Semaphore, Pipes, was auch immer.
Die sind threadsicher, so dass du damit keine Probleme bekommst. Am sinnvollsten wäre wie gesagt WaitForMultipleObjects (oder auch WaitForSingleObject, je nachdem). Damit kannst du auf das Event einfach warten.
Dennoch kannst du aber zusätzlich auf ein Abbruchevent reagieren, so dass der Thread bei Programmende z.B. nicht hängt, weil er immer weiter wartet.
Wie das geht:
Du erstellst mit CreateEvent ein neues Event. Da bekommst du ein Handle zu diesem Event. Dieses kannst du dann an WaitFor... übergeben. Jetzt kannst du mit SetEvent das Event auslösen und die Wartefunktion wird beendet. Der Rückgabewert zeigt dir an, ob das Event ausgelöst wurde (und beim Warten auf mehrere Events welches Event ausgelöst wurde).
// EDIT:
Ein Beispiel siehst du hier live in action  :
www.koders.com/delph...B691E4FB40A3427.aspx
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 21.11.10 20:08
Hey,
so ganz bin ich noch nich dahinter gestiegen. Mal zu meinem jetzigen Wissenstand. Der Thread arbeitet. Jetzt muss er auf nen anderen Thread warten, erzeugt das Event, ruft WaitFor auf und Wartet. Der andere Thread ruft dann SetEvent auf, wenn er fertig ist un der 1. Thread areitet daraufhin weiter. Soweit richtig?
Jetzt zum eigentlichen Problem: Ich Klick auf meinen Button 'Pause' (im MainThread) und der ArbeitsThread soll warten. Wie bzw. wo leg ich da jetzt das Event an?!
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 21.11.10 20:20
Bergmann89 hat folgendes geschrieben : | Jetzt muss er auf nen anderen Thread warten, erzeugt das Event, ruft WaitFor auf und Wartet. Der andere Thread ruft dann SetEvent auf, wenn er fertig ist un der 1. Thread areitet daraufhin weiter. |
Hier reicht WaitFor auch. Denn hier wartest du ja ohnehin bis der andere fertig ist.
Du kannst aber auch ganz ohne WaitFor ein Event erzeugen und stattdessen den zweiten Thread mittels SetEvent den ersten benachrichtigen lassen. Dann kann der mit WaitForMultipleObjects sowohl auf den zweiten Thread als auch auf einen Abbruch warten.
Bergmann89 hat folgendes geschrieben : | Jetzt zum eigentlichen Problem: Ich Klick auf meinen Button 'Pause' (im MainThread) und der ArbeitsThread soll warten. Wie bzw. wo leg ich da jetzt das Event an?! |
Der Arbeitsthread erzeugt im Konstruktor das Event und bietet das Handle entweder als Property an oder bietet eine Methode an, die das Event auslöst.
Dann führt der Arbeitsthread immer seine Aufgabe durch und wartet dann auf das Event. Am besten wartet er zusätzlich auf ein Abbruchevent.
Der Hauptthread setzt die Werte für den aktuellen Job und stößt dann das Event an. Der Thread führt den Job aus und wartet wiederum auf den nächsten Job.
Je nachdem was wie ablaufen soll, ist es anders günstiger. Grundsätzlich muss der Thread selbst benachrichtigt werden, dass etwas passieren soll und nicht so außen gezwungen werden anzuhalten oder so. Der Thread bekommt die Nachricht und führt das entsprechend aus.
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 21.11.10 20:34
Hey,
k auch verstanden, ABER: mein Thread hat seine Aufgabe schon bekommen, und er arbeitet jetzt unermüdlich (evtl. mehrere Stunden). Und jetzt soll der User die Möglichkeit haben den zu pausieren. Wenn ich jetzt in der Rechenroutine des Threads immer wieder ein WaitFor drin hab, dann muss der User (bzw. die MainThread) die Weiterarbeit immer wieder anstoßen.
Oder ichhab dich doch falsch verstanden
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 21.11.10 20:39
Nein, das passt schon. Du kannst einfach ein Timeout von 0 angeben. Dann prüfst du nur den Status, wartest aber nicht darauf.
Am Rückgabewert erkennst du dann ob abgebrochen wurde (Event 1), pausiert werden soll (Event 2) oder das Ergebnis Timeout ist (kein Event wurde signalisiert).
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 21.11.10 21:52
Hey,
habs hinbekommen (glaub ich). Zumindest hängt er sich nicht mehr auf.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51:
| Thread = class(TThread) private fPauseEvent: Cardinal; public property PauseEvent: Cardinal read fPauseEvent; procedure Execute; override; constructor Create(Suspended: Boolean); destructor Destroy; override; end;
procedure Thread.Execute; var Finished: Boolean; begin Finished := False; while not Finished do begin if WaitForSingleObject(fPauseEvent, 0) = WAIT_OBJECT_0 then Suspend; if Terminated then Finished := True; end; end;
constructor TGenTipPlanThread.Create(Suspended: Boolean); begin inherited Create(Suspended); fPauseEvent := CreateEvent(nil, True, False, ''); ResetEvent(fPauseEvent); end;
destructor TGenTipPlanThread.Destroy; begin CloseHandle(fPauseEvent); inherited Destroy; end;
procedure TForm.SuspendThreadBtClick(Sender: TObject); begin if Assigned(Thread) then SetEvent(Thread.PauseEvent); end;
procedure TForm.ResumeThreadBtClick(Sender: TObject); begin if Assigned(Thread) then if Thread.Suspended then begin ResetEvent(Thread.PauseEvent); Thread.Resume; end; end; |
Is das soweit richtig oder hat noch jmd Verbesserungsvorschläge?
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 21.11.10 22:11
Du solltest ganz auf das Suspend verzichten. Du kannst genauso auf die neue Aufgabe warten wie auf das Pausieren prüfen (nur da eben ohne Timeout, einfach warten auf Abbruch oder neue Aufgabe).
Das hat den Vorteil, dass beim Beenden des Programms der Thread nicht hängt und auch nicht zum Beenden extra aufgeweckt werden muss.
So wie du es jetzt hast, sollte es aber auch klappen vorausgesetzt du hast FreeOnTerminate nicht auf True gesetzt. Denn sonst gibt es in Kombination mit Resume evtl. ziemliche Probleme.
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: So 21.11.10 22:45
Hey,
aber er soll doch keine neue Aufgabe bekommen, sondern genau an der Stelle weiter rechnen?! FreeOnTerminate steht auch auf True, bis jetzt gings aber.
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 21.11.10 23:46
Bergmann89 hat folgendes geschrieben : | aber er soll doch keine neue Aufgabe bekommen, sondern genau an der Stelle weiter rechnen?! |
Das ist ja egal, ob der Thread nun eine neue Aufgabe bekommt oder die alte weitermacht.
Du kannst aber jedenfalls an der Stelle einfach nach dem Erhalt des Pausensignals auf den Abbruch oder das Fortsetzen warten. Das sind zwei Events, auf die du mit WaitForMultipleObjects parallel warten kannst.
Bergmann89 hat folgendes geschrieben : | FreeOnTerminate steht auch auf True, bis jetzt gings aber. |
Wenn du Resume aufrufst und den Thread dann beendest, kann es z.B. passieren, dass der weiterlaufende Thread sich bereits beendet hat bevor Resume fertig ist. Denn der Thread wird darin ja fortgesetzt, aber Resume ist dann ja nicht sofort beendet.
Es kann also passieren, dass Resume erst danach fertig wird und noch auf den Thread zuzugreifen versucht, der bereits zerstört wurde. Und dann knallt es.
Besser ist es den Thread kontrolliert selbst aus dem Speicher zu entfernen, wenn er nicht mehr gebraucht wird.
|
|
Bergmann89 
      
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: Mo 22.11.10 01:23
Hey,
jetzt machts klick, ich kann ja auch unendlich lange warten. Das hab ich grad irgendwie verdrängt  Das werd ich dann morgen umsetzen, is doch schon recht spät. Nochma vielen Dank für die Hilfe.
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
|