| 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 06.02.11 14:55
Hey,
ich arbeite zur Zeit an einer Art ThreadPool, der die Threads bei einer bestimmten Berechnung verwaltet und verschiedene Methoden für die Threads zur verfügung stellt. Funktioniert alles wunderbar, bis auf die Freigabe. Denn sobald ich einen Thread freigeben will hängt sich mein Programm komplett auf.
Zur Arbeitsweiße des ThreadPools: Man erzeugt den Pool und übergibt ihm verschiedenen Daten/Objekte. Dann kann man die Berechnung mit einer Methode anstoßen. In dieser Methode werden die Threads erzeugt und nehmen dann ihre Arbeit auf. Wenn die Berechnungen abgeschlossen sind (oder der User die Berechnung unterbricht) wird die OnTerminate-Methode (die vom ThreadPool bereitgestellt wird) der Threads aufgerufen. In dieser Methode wird geprüft, ob alle Threads beendet sind. Wenn dem so ist, dann wird das OnFinish-Event des ThreadPools aufgerufen. In dieser Methode wird der ThreadPool dann auch freigegeben, und es kommt zum DeadLock. Wenn ich den ThreadPool erst freigebe, wenn das Programm geschlossen wird, dann funktioniert alles 1a. Die Freigabe erfolgt in beiden Fällen im Context des MainThreads. Ich denke es liegt daran, das die Freigabe aus dem OnTerminate des Threads aufgerufen wird. Ich weiß aber nicht wie ich es besser lösen soll. Ich hab auch schon versucht ne Message an den Hauptthread zu schicken und die Freigabe dann da zu machen, das geht aber auch nicht. Wäre toll wenn mir da mal jmd aushelfen könnte.
Eine Idee hab ich noch, aber ich bezweifle das es so richtig ist: Ich könnte den ThreadPool auch zum Thread machen, der dann solange auf ein Event wartet, bis alle ArbeitsThreads beendet sind. Dann rüft der ThreadPool sein OnTerminate auf und wird durch FreeOnTerminate freigegeben...
MfG & Thx Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 06.02.11 16:24
Da hier grad etwas Soße fehlt, rate ich mal in's Blaue:
Du rufst in deinem Thread Syncronize(IMDone) auf und wartest im OnFinish des ThreadPools mit WaitForSingleObject auf eben diesen Thread?
Nutzt Du die Thread-Pool-API von Windows oder implementierst Du die Verwaltung vollständig selber?
Hast Du in deinen Threads zum Zeitpunkt des Aufrufes der Terminate-Prozedur gerade irgendwelche Critical Sections offen? Oder andere blockierende Locks (Semaphoren, Mutexe, Blocking IO).
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
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 06.02.11 16:56
Hey,
ich warte bis der Thread fertig gearbeitet hat, dann ruft ja das TThread-Objekt sein OnTerminate auf (hab also keine eigene Methode). Im OnTerminate prüfe ich dann, ob alle Threads beendet sind, wenn ja, dann wird das OnFinish vom ThreadPool aufgerufen, in dem der ThreadPool dann freigegeben werden soll.
Ich hab das alles selber implementiert, wusste nich, das es da schon was fertiges von Windows gibt.
Ich hab ein paar Semaphoren, es könnte auch sein, das ein Thread gerade in dem geschützten Bereich ist, wenn ein anderer seine OnTerminate ausführt. Aber da ich ja im OnTerminate prüfe ob alle Threads fertig sind befindet sich bei der Freigabe kein Thread in einem geschützen Bereich, weil sie ja vorher fertig grechnet haben oder ordnungsgemäß beendet wurden.
Im Anhang hab ich mal das wichtigste zusammengefasst. Um das ganze nochmal kurz zu erläutern: Der ThreadManager erzeugt die Thread und gibt sie am Ende auch wieder frei. Weiterhin stellt er die Methoden "NextTipPlan" und "AddTipPlanData" für die Threads zur Verfügung (in denen werden auch die Semaphoren eingesetzt). NextTipPlan stellt neue Daten zur verfügung mit denen der Thread was berechnen soll. Wenn die Berechnungen erfolgreich sind und in den geforderten Wertebereichen liegen, dann ruft der Thread die AddTipPlanData-Methode des Managers auf. Mit dieser Methode werden die Daten dann in einer Liste abgespeichert.
Wenn NextTipPlan keine neuen Daten mehr erzeugen kann oder wenn der User die Aktion abbricht, dann wird der Thread beendet und die GenThreadTerminate-Methode des Managers wird ausgeführt.
MfG Bergmann.
Einloggen, um Attachments anzusehen!
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: So 06.02.11 17:12
hmmm, je nach Dauer der Berechnungen könnte man also hier über NextTipData auch das vorzeitige Beenden realisieren, indem man einfach, wenn ein bestimmtes Flag gesetzt ist einfach vortäuscht, dass keine Daten mehr vorhanden sind.
Das Beenden mit Warten (was für ein User Cancel nötig wäre) wäre dann darauf begrenzt mit einer Schleife darauf zu warten, dass kein Thread mehr aktiv ist.
Edit: Im Thread.Execute ruft man Terminate nicht auf, sondern von außerhalb, weil Terminated für den Thread als Abfragemöglichkeit bereitgestellt wurde. Ob der Thread terminiert ist, bekommt man über die WinAPI raus, wenn man abfragt, ob das Thread-Handle signalled ist. Bin noch am Lesen des Sources.
Edit 2: Wenn destructor TTipPlanGenThread.Destroy; wirklich nur den Parent aufruft, kann das weggelassen werden.
Edit 3: Lass die Thread wenn möglich selber sich terminieren und geb von außen nur Steuerkommandos rein. Auch scheint mir deine Synchronisierung für das NextPlanData recht seltsam.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
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 06.02.11 20:06
Hey,
das beenden funktioniert ja, nur bei der Freigabe der Thread-Objekte kommt dann der Deadlock. Ich denke das liegt daran, das ich das Free aus dem OnTerminate-Event des Threads heraus aufrufe (wenn auch nicht direkt). Die Aufrufe sind so:
Quelltext 1: 2: 3: 4:
| TATGThreadManager.GenThreadTerminate (OnTerminate-Event der Threads) TAutoTipPlanGenForm.ThreadManagerFinish (OnFinish-Event des ThreadManagers) fTreadManager.Free; fTipPlanGenArr[i].Free //und hier kommts zum Deadlock |
Aber wenn ich den destructor beim beenden des Programms aufrufe funktioniert alles. Die Frage ist nun, wie ich die Threads nach dem OnTerminate-Event von ausherhalb freigeben kann. Mit ner extra Schleife würd ich das so machen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| type TATGThreadManager = class(TThread) private fTerminateEvent: Cardinal; public procedure TerminateAll; procedure Execute; override; end;
procedure TATGThreadManager.TerminateAll; begin SetEvent(fTerminateEvent); end;
procedure TATGThreadManager.Execute; begin WaitForSingleObject(fTerminateEvent, INFINITE); FreeOnTerminate := True; end; |
so könnte ich in GenThreadTerminate einfach TerminateAll aufrufen, wenn alle Thread terminiert sind und der manager gibt sich selbst und die Threads frei. Aber irgendwie gefällt mir die Lösung nich, ich hab das Gefühl, das es da was besseres gibt, oder?
BenBE hat folgendes geschrieben : | | Edit: Im Thread.Execute ruft man Terminate nicht auf, sondern von außerhalb, weil Terminated für den Thread als Abfragemöglichkeit bereitgestellt wurde. Ob der Thread terminiert ist, bekommt man über die WinAPI raus, wenn man abfragt, ob das Thread-Handle signalled ist. Bin noch am Lesen des Sources. |
Wie fragt man das ab? Ich hätt es jetzt mit if WaitForSingleObjects(ThreadHandle, 0) = WAIT_OBJECT_0 then gemacht.
BenBE hat folgendes geschrieben : | | Edit 2: Wenn destructor TTipPlanGenThread.Destroy; wirklich nur den Parent aufruft, kann das weggelassen werden. |
is für später noch mehr geplant, deshalb is der schonmal eingebaut.
BenBE hat folgendes geschrieben : | | Lass die Thread wenn möglich selber sich terminieren und geb von außen nur Steuerkommandos rein. Auch scheint mir deine Synchronisierung für das NextPlanData recht seltsam. |
Die Threads terminieren sich doch alle selber, von draußen werden bloß die Events gesetzt (fAbortEvent mit der Abort-Methode). Für das synchronisieren is mir keine bessere Methode eingefallen. Hab auch noch nicht so oft mit Threads gearbeitet. Wie macht man das denn normalerweiße? Ich hab einfach nur den Bereich, mit den alle Threads arbeiten müssen (Code + Variablen) in ein anderes (gemeinsames) Objekt verfrachtet und die Methode dann mit nem Semaphor threadsicher gemacht.
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
|