Einzelnen Beitrag anzeigen

alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#5

Re: Thread .Terminate beim Beenden - Runtime Error 216

  Alt 12. Aug 2009, 09:42
Das kontrollierte Beenden von Threads kann Einem schon mal den Schlaf rauben. Ich verzichte grundsätzlich auf Resume/Suspend und arbeite stattdessen lieber mit Synchronisationsobjekten (Semaphoren). Weiterhin verzichte ich auf 'FreeOnTerminate' und gebe Threads immer kontrolliert frei. Tut man das nicht, kann es sein, das einem die Applikation beim Programmende um die Ohren fliegt. Im Finalization-Abschnitt von 'Classes.Pas' werden nämlich Resourcen freigegeben, die von der Terminierungssequenz eines Threads benötigt werden.

Ich schreibe mir immer eine Stop-Methode zu meinen Threads, damit der Thread kontrolliert terminiert. Danach kann ich den Thread beruhigt freigeben.

Diese Stop-Methode macht Folgendes:
1. "Terminate" aufrufen. Damit wird ja nur das 'Terminated'-Flag gesetzt.
2. Sicherstellen, das die Execute-Methode terminiert.
3. Mit "WaitFor" warten, bis die Terminierungssequenz des Threads abgearbeitet ist.

Da ich mit Semaphoren arbeite, sieht die Execute-Methode immer gleich aus;
Delphi-Quellcode:
Procedure TMyThread.Execute;
Begin
  // Lokale Resourcen initialisieren, ggf. CoInitialize aufrufen, wenn mit COM gearbeitet wird
  Try
    While Not Terminated Do Begin
      WaitForSingleObject(fSemaphore,INFINITE);
      If Terminated Then Break;
      // Arbeit durchführen. Meist einen Job aus einer Queue holen und abarbeiten
    End;
  Finally
  // Lokale Resourcen wieder freigeben
  End
End;
Den Thread füttere ich mit Arbeit über eine AddJob-Methode, die den Job in die Queue schiebt und die Semaphore per 'ReleaseSemaphore' anschalte.

Damit reicht es, unter (2) 'ReleaseSemaphore' aufzurufen, denn dann wacht der Thread auf, merkt das er terminieren soll und macht das auch.
Delphi-Quellcode:
Procedure TMyThread.Stop;
Begin
  Terminate; // Flag setzen
  ReleaseSemaphore(fSemaphore,1,nil); // 'Arbeit beenden' anfordern
  WaitFor; // Warten, bis Arbeit beendet ist
End;
Falls mein Thread noch mitten im Abarbeiten von Jobs steckt, die sich in der Queue angesammelt haben, macht das nichts. Er wird in jedem Fall nach Beendigung des aktuellen Jobs terminieren. Will ich das nicht, habe ich einen speziellen 'Ende-Job', den ich in die Queue schiebe. Dann werden zunächst alle Jobs abgearbeitet, dann kommt der Ende-Job und die 'Execute'-Methode hört auf.
Delphi-Quellcode:
Procedure TMyThread.Stop;
Begin
  Add (TEndThreadJob.Create); // Ende-Job in die Queue schieben
  WaitFor; // Warten, bis Arbeit beendet ist
End;
Von außen sieht die Arbeit mit meinen Threads also immer gleich aus: Erzeugen, mit Job füttern, Stop aufrufen, freigeben.


Ach, warum ich nie mit Release/Suspend arbeite? Ich stell mir das so vor: Ich habe ja einen Arbeiter, der im Hintergrund Sachen für mich erledigt. Mit Suspend brate ich ihm eins über, sodaß er auf der Stelle ohnmächtig wird. Was ist aber, wenn er gerade in einer wichtigen Sache steckt? Blöd gelaufen. Ich muss also immer auf der Hut sein, ob und wann ich ihm eins verpasse.

Es geht natürlich so, speziell wenn nur der Thread selbst sich eins vor die Rübe knallt, aber mir liegen diese Semaphoren einfach besser. Bildlich gesehen ackert mein Arbeiter im Nebenraum und ich schiebe ihm durch ein Fenster Zettel zu, auf denen steht, was er bei als nächstes zu tun hat. Irgendwie humaner, finde ich.

Na ja. Arbeiter eingesperrt im Nebenraum, das ruft auch die Gewerkschaft auf den Plan...
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat