Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Arbeiten mit TThreadList (https://www.delphipraxis.net/176021-arbeiten-mit-tthreadlist.html)

Captnemo 7. Aug 2013 09:36

Delphi-Version: XE2

Arbeiten mit TThreadList
 
Ich schreibe gerade an einem Programm, in dem eine vom Benutzer festgelegte Anzahl an Threads starten sollen. Da ich bei Programmende alles schön wieder aufräumen will, muß ich ja dafür sorgen, dass alle Threads brav beendet sind.
Kann ich dafür die TThreadList verwenden?

Funktioniert das ähnlich wie eine TObjectList?
Also TObject->TObjectList und TThread->TThreadList?

Gibt's irgendwo ein schickes Tutorial, dass man auch versteht, wenn bisher wenig mit TThreads gearbeitet hat?

Oder gibt's eine bessere oder einfachere Möglichkeit, die TThreads in einer Liste zu verwalten, und diese beim Beenden abzuarbeiten.

Sir Rufo 7. Aug 2013 09:40

AW: Arbeiten mit TThreadList
 
Einfach in eine TObjectList packen, aber beim Aufräumen die Threads beenden, auf Fertigstellung warten und dann wegwerfen.

Eine Delphi-Referenz durchsuchenTThreadList ist dafür gedacht, dass mehrere Threads in diese Liste greifen können (und zwar mit Delphi-Referenz durchsuchenTThreadList.LockList)

Achtung: Das Codebeispiel bitte nur unter dem Aspekt TThtreadList betrachten, ansonsten ist das ein absolutes NoGo-Beispiel

Captnemo 7. Aug 2013 09:46

AW: Arbeiten mit TThreadList
 
Ah, cool. Danke für deine schnelle Hilfe.

Union 7. Aug 2013 10:13

AW: Arbeiten mit TThreadList
 
Was willst Du denn mit der Begrenzung erreichen? Etwa eine Steuerung der Auslastung?

Captnemo 7. Aug 2013 10:28

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Union (Beitrag 1223835)
Was willst Du denn mit der Begrenzung erreichen? Etwa eine Steuerung der Auslastung?

Was für eine Begrenzung?
Ich will nur wissen, wie viele Thread laufen, damit ich die bei Programmende sauber beenden kann.

Union 7. Aug 2013 10:33

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Captnemo (Beitrag 1223821)
... in dem eine vom Benutzer festgelegte Anzahl an Threads starten sollen

Daher kam meine Frage.

Captnemo 7. Aug 2013 11:52

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Union (Beitrag 1223838)
Zitat:

Zitat von Captnemo (Beitrag 1223821)
... in dem eine vom Benutzer festgelegte Anzahl an Threads starten sollen

Daher kam meine Frage.

Genau genommen geht es darum, seriell angeschlossene Geräte abzufragen. Wieviele angeschlossen werde, dass wird vom User eingestellt. Es können also 3 aber auch 10 sein. Ich selbst begrenze die Anzahl nicht.

Klaus01 7. Aug 2013 12:40

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Captnemo (Beitrag 1223848)
Zitat:

Zitat von Union (Beitrag 1223838)
Zitat:

Zitat von Captnemo (Beitrag 1223821)
... in dem eine vom Benutzer festgelegte Anzahl an Threads starten sollen

Daher kam meine Frage.

Genau genommen geht es darum, seriell angeschlossene Geräte abzufragen. Wieviele angeschlossen werde, dass wird vom User eingestellt. Es können also 3 aber auch 10 sein. Ich selbst begrenze die Anzahl nicht.

Die Anzahl der quasi parallel laufenden Threads sollte m.E. abhänging von der Anzahl der CPUs/der cores sein und nicht zwingend von der Anzahl der abzufragenden Geräte.

Grüße
Klaus

Captnemo 7. Aug 2013 13:08

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Klaus01 (Beitrag 1223855)
Zitat:

Zitat von Captnemo (Beitrag 1223848)
Zitat:

Zitat von Union (Beitrag 1223838)
Zitat:

Zitat von Captnemo (Beitrag 1223821)
... in dem eine vom Benutzer festgelegte Anzahl an Threads starten sollen

Daher kam meine Frage.

Genau genommen geht es darum, seriell angeschlossene Geräte abzufragen. Wieviele angeschlossen werde, dass wird vom User eingestellt. Es können also 3 aber auch 10 sein. Ich selbst begrenze die Anzahl nicht.

Die Anzahl der quasi parallel laufenden Threads sollte m.E. abhänging von der Anzahl der CPUs/der cores sein und nicht zwingend von der Anzahl der abzufragenden Geräte.

Grüße
Klaus

Also darüber brauche ich ganz sicher keine Sorgen machen. Zum einen werde niemals mehr als 12 Geräte angeschlossen (das ist in diesem Anwendungsfall mal absolut sicher), und zum anderen liefern die Geräte nur in Intervallen von 10 Sekunden Daten, evtl. sogar mit viel größeren Abständen.
Dem zur Folge für meine Zwecke völlig okay. Ich hab's grad mal mit 20 Threads auf einem 1 Prozessorsystem getestet und hatte 0,1% Auslastung. Laufen wird es nachher auf einem 8-Core-System.

[OT on]
Hey, danke für eurer Interesse. Aber mir ging es tatsächlich um die Frage, die schon im ersten Thread stand. Nicht darum, ob und warum man die Threadanzahl begrenzen sollte. Solche Diskussionen ziehen diesen Beitrag nur unnötig in die Länge und nützen am Ende keinem. Außerdem wären dafür noch ganz andere Informationen über den Sinn und Zweck des Programm und der Threads notwendig.

Jetzt aber nicht gleich beleidigt sein, okay :-D

Daniel hatte es für mich mit seiner Antwort schon wie immer sehr treffend auf dem Punkt gebracht. :thumb::thumb:

[OT off]

Sir Rufo 7. Aug 2013 13:13

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Captnemo (Beitrag 1223857)
Daniel hatte es für mich mit seiner Antwort schon wie immer sehr treffend auf dem Punkt gebracht. :thumb::thumb:

Wann hat der denn hier geantwortet? :gruebel:

Captnemo 7. Aug 2013 18:40

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Sir Rufo (Beitrag 1223858)
Zitat:

Zitat von Captnemo (Beitrag 1223857)
Daniel hatte es für mich mit seiner Antwort schon wie immer sehr treffend auf dem Punkt gebracht. :thumb::thumb:

Wann hat der denn hier geantwortet? :gruebel:

Oh mein Gott:twisted::twisted:

Meinte natürlich dich :-D:-D Verd*mt, hab euch glatt verwechselt. Wie peinlich :lol::lol:

Also, natürlich hast du es auf den Punkt gebracht. Großes Sorry

Captnemo 23. Jun 2014 12:24

AW: Arbeiten mit TThreadList
 
Hi, ich muß diese Thema noch mal aufwärmen, da ich da noch ein paar Probleme habe.

Also ich erzeuge mir eine TThreadList, die ich beim Beenden des MainThread entfernen möchte.

Delphi-Quellcode:
procedure Tfrm_main.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  I: Integer;
  r: Cardinal;
begin
  for I := RS232ThreadList.Count-1 downto 0 do
  begin
    TComThread(RS232ThreadList[i]).StopListen;
    TComThread(RS232ThreadList[i]).Terminate;
    r:=WaitForSingleObject(TComThread(RS232ThreadList[i]).Handle, 1000);
    //ShowMessage(IntToStr(r));
  end;
end;

procedure Tfrm_main.FormCreate(Sender: TObject);
begin
  RS232ThreadList:=TObjectList.Create;
end;

procedure Tfrm_main.FormDestroy(Sender: TObject);
begin
  RS232ThreadList.Free;
end;
Sicherlich nicht korrekt, denn im FormDestroy kommt es beim RS232ThreadList.Free zu einer Exception, da die Thread noch nicht beendet sind.

Bzw. genau genommen, lande ich in System.Classes im Destructor TThread.Destroy beim CloseHandle;
Meine Annahme, die Threads in der ThreadList sind noch nicht beendet.

Wie mache ich es richtig? Ich muß ja im OnCanClose auf das Beenden der Threads warten.

Uwe Raabe 23. Jun 2014 12:43

AW: Arbeiten mit TThreadList
 
Zeig mal

a) wie du die Threads erzeugst (FreeOnTerminate?)
b) wie du die Liste erzeugst (OnwsObjects?)

Dejan Vu 23. Jun 2014 13:01

AW: Arbeiten mit TThreadList
 
Wieso wartest Du nur maximal eine Sekunde auf das Handle? Warte doch lieber, bis das Handle die Fahne oben hat, auch wenn's dauert...
Und falls es sich dabei um Threads handelt, würde ich mit 'WaitFor' noch warten, bis das Teil auch wirklich beendet ist und es dann ggf. explizit per Free freigeben (außer, das macht die ThreadListe selbst)

Captnemo 23. Jun 2014 13:29

AW: Arbeiten mit TThreadList
 
Mit OwnObject:=False funktionierts. FreeOnTerminate ist True.

Jetzt werden die Objecte (Thread) also freigegen, wenn die Liste freigegeben werden.
Aber eigentlich müßte es ja auch anders gehen.

Ist denn das mit Waitforsingleobject so korrekt? Was ist eigentlich der Rückgabewert von Waitforsingleobject? In meinem Fall habe ich immer 0 zurück bekommen.

Das mit 1000 Milisekunden ist erst mal zum Testen gewesen.

Mit WaitFor on OnCanClose bekomme ich immer die EThread-Exception "Das Handle ist ungültig(6)".

Der schöne Günther 23. Jun 2014 13:51

AW: Arbeiten mit TThreadList
 
Das ist die
Delphi-Quellcode:
TThreadList
aus
Delphi-Quellcode:
System.Classes
oder
Delphi-Quellcode:
System.Generics.Collections
, oder? Ich kann der nichts abgewinnen: Da fehlen elementarste Dinge wie ein
Delphi-Quellcode:
GetEnumerator()
und alles. Bist du sicher, dass du die brauchst? Ich hätte gedacht, dass du die RS232-Threads alle im Hauptthread erstellst und an die Liste dranhängst und ebenso dort auch die Liste wieder zumachen willst?

Ich nehme da einfach eine ganz "normale" Objektliste (
Delphi-Quellcode:
TObjectList
)- Soweit die
Delphi-Quellcode:
OwnsObjects = True
hat, ist die Freigabe der Liste mitsamt aller enthaltenen Threads so einfach wie ein
Delphi-Quellcode:
meineListe.Free();
Bsp:
Delphi-Quellcode:
program Project4;

{$APPTYPE CONSOLE}
{$R *.res}

uses
   System.SysUtils,
   System.SyncObjs,
   System.Classes,
   System.Generics.Collections;

const
   numThreads: Integer = 5;

var
   threadList: TObjectList<TThread>;
   threadNum: Integer;
   threadIterator: TThread;
   consoleCs: TSynchroObject;

//{$DEFINE OWNSOBJECTS}

procedure writeConsole(const line: String);
begin
   consoleCs.Acquire();
   try
      WriteLn(line);
   finally
      consoleCs.Release();
   end;
end;

function constructRunningThread(const threadNum: Integer): TThread;
begin
   Result := TThread.CreateAnonymousThread(
      procedure
      begin
         writeConsole('Thread ' + threadNum.ToString() + ' startet...');
         sleep(1000 + Random(4000));
         writeConsole('Thread ' + threadNum.ToString() + ' endet...');
      end
   );
   Result.FreeOnTerminate := {$IFDEF OWNSOBJECTS}False{$ELSE}True{$ENDIF};
   Result.Start();
end;

begin
   try
      consoleCs := TCriticalSection.Create();

      writeConsole('Erstelle und fülle Liste...');
      threadList := TObjectList<TThread>.Create(
         {$IFDEF OWNSOBJECTS}True{$ELSE}False{$ENDIF}
      );
      for threadNum := 0 to Pred(numThreads) do
         threadList.Add(constructRunningThread(threadNum));

      writeConsole('Baue Liste ab...');
      threadList.Destroy();
      writeConsole('Liste abgebaut');
      writeConsole('<Taste drücken>');
   except
      on E: Exception do
         WriteLn(E.ClassName, ': ', E.Message);
   end;

   ReadLn;
end.


// Roter Kasten:
Entweder ein TThread hat FreeOnTerminate = True und du fasst ihn nach dem Starten nicht mehr an(!) oder er hat es auf False und du gibst die TThread-Instanz selbst so frei wie du möchtest. Ich habe das mal versucht in das Beispiel zu packen: Du kannst das
Delphi-Quellcode:
{$DEFINE OWNSOBJECTS}
einmal auskommentieren und dir den Unterschied anschauen :-)

Uwe Raabe 23. Jun 2014 13:59

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Captnemo (Beitrag 1263254)
Mit OwnObject:=False funktionierts. FreeOnTerminate ist True.

Wenn FreeOnTerminate = true ist, dann gibt sich der Thread beim Beenden selbst frei und der Eintrag in der Liste zeigt auf diese eventuell gerade freigegebene Instanz- Das ist böse!

Besser FreeOnTerminate auf false und dafür OwnsObjects auf true. Dann bleiben die Instanzen der Threads auch nach dem Beenden gültig und werden erst mit der Freigabe der Liste auch freigegeben.

Captnemo 23. Jun 2014 14:32

AW: Arbeiten mit TThreadList
 
Danke, FreeOnTerminate hab ich auf False gesetzt.

@Günther: Das werd ich mir auch noch mal genau anschauen.

Aber das mit WaitForSingleObject hab ich noch nicht verstanden. Rückgabewert hab ich in OH gefunden.
Aber mit dem Satz "The state of the specified object is signaled" ist mir noch nicht ganz klar. Heißt das jetzt, WaitForSingleObject wartet auf irgendeine Reaktion vom Object?

pertzschc 23. Jun 2014 14:48

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1263256)
Ich habe das mal versucht in das Beispiel zu packen

Mit welchem Delphi läuft Dein Code? Bei mir im Delphi 2010 geht z.B. das TThread.CreateAnonymousThread nicht.

Danke für einen Hinweis,
Christoph

Uwe Raabe 23. Jun 2014 14:56

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Captnemo (Beitrag 1263260)
Aber das mit WaitForSingleObject hab ich noch nicht verstanden. Rückgabewert hab ich in OH gefunden.
Aber mit dem Satz "The state of the specified object is signaled" ist mir noch nicht ganz klar. Heißt das jetzt, WaitForSingleObject wartet auf irgendeine Reaktion vom Object?

WaitForSingleObject wartet auf ein bestimmtes Ereignis. Welches das ist, hängt ganz vom Object (Event, Timer, Mutex) ab. Ein Thread löst das Signal beim Beenden aus.

Der schöne Günther 23. Jun 2014 17:35

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von pertzschc (Beitrag 1263263)
Zitat:

Zitat von Der schöne Günther (Beitrag 1263256)
Ich habe das mal versucht in das Beispiel zu packen

Mit welchem Delphi läuft Dein Code? Bei mir im Delphi 2010 geht z.B. das TThread.CreateAnonymousThread nicht.

Das war XE5 oder XE6, weiß ich nicht mehr.
Vielleicht kam das erst mit XE?
Aber ansonsten wäre es kein Unterschied, stattdessen mit (weitaus mehr Tippaufwand) sich auf klassischem Wege wieder eine Klasse
Delphi-Quellcode:
TMyThread = class(TThread)
zu definieren,
Delphi-Quellcode:
Execute()
zu implementieren und den letztendlich zurückzugeben.

Sir Rufo 23. Jun 2014 19:01

AW: Arbeiten mit TThreadList
 
Ich verstehe gar nicht, was dieses Gehampel mit den Threads soll.
  1. Ein Thread sollte sich bei Delphi-Referenz durchsuchenTThread.Terminate von selber auch sauber beenden (muss nicht schlagartig sein, aber er sollte sich ab jetzt nur noch Augen für das Sandmännchen haben)
  2. Delphi-Referenz durchsuchenTThread.FreeOnTerminate - habe ich mal benutzt, ist aber ewig her, weil es mehr Probleme schafft als wirklich hilft
Solche Threads packt man einfach in eine ganz normale
Delphi-Quellcode:
TObjectList
(ja, OwnsObjects auf True) und wenn diese Threads aus dem Speicher sollen, dann ein ganz lapidares freigeben der Liste.

Ein Thread ruft im Destroy ganz von alleine das Delphi-Referenz durchsuchenTThread.Terminate auf, dann ein
Delphi-Quellcode:
TThread.WaitFor
.

Fazit: Die Thread-Klasse sauber aufbauen, dann klappt das wie von selber

Dejan Vu 25. Jun 2014 10:42

AW: Arbeiten mit TThreadList
 
Zitat:

Zitat von Sir Rufo (Beitrag 1263291)
Ein Thread ruft im Destroy ganz von alleine das Delphi-Referenz durchsuchenTThread.Terminate auf, dann ein
Delphi-Quellcode:
TThread.WaitFor
.

Fazit: Die Thread-Klasse sauber aufbauen, dann klappt das wie von selber

Na ja. Ich hatte immer Probleme (vielleicht älteres Delphi), einen Thread einfach so freizugeben. Erst seitdem ich Terminate,WaitFor,Free aufrufe, klappt es. Vielleicht ist das ja auch Aberglaube.

Bjoerk 25. Jun 2014 10:55

AW: Arbeiten mit TThreadList
 
Apropos WaitFor. Darf ich mich mal kurz einklinken? Kann man das auch in den destructor schreiben oder ist es da schon zu spät?
Delphi-Quellcode:
constructor TFindSnapPointsThread.Create(List: TGraphicList);
begin
  inherited Create(true);
  FreeOnTerminate := true;
  FFindSnapPoints := TFindSnapPoints.Create;
  FList := List
end;

destructor TFindSnapPointsThread.Destroy;
begin
  WaitFor;
  FFindSnapPoints.Free;
  inherited Destroy;
end;

procedure TFindSnapPointsThread.Terminate;
begin
  FFindSnapPoints.Cancel := true;
  inherited Terminate;
end;

Sir Rufo 25. Jun 2014 17:50

AW: Arbeiten mit TThreadList
 
Es sollte reichen, wenn du die Instanzen nach dem
Delphi-Quellcode:
inherited
freigibst.
Delphi-Quellcode:
destructor TFindSnapPointsThread.Destroy;
begin

  inherited;
  FFindSnapPoints.Free;
end;
Um hier aber etwas wirklich konkretes zu sagen, musst du dir dringend einmal anschauen, was in
Delphi-Quellcode:
TThread.Destroy
so passiert.

In den neueren Delphi-Versionen wird dort eben genau das alles gemacht (
Delphi-Quellcode:
Terminate
,
Delphi-Quellcode:
WaitFor
,...).

Zitat:

Zitat von Dejan Vu (Beitrag 1263434)
Zitat:

Zitat von Sir Rufo (Beitrag 1263291)
Ein Thread ruft im Destroy ganz von alleine das Delphi-Referenz durchsuchenTThread.Terminate auf, dann ein
Delphi-Quellcode:
TThread.WaitFor
.

Fazit: Die Thread-Klasse sauber aufbauen, dann klappt das wie von selber

Na ja. Ich hatte immer Probleme (vielleicht älteres Delphi), einen Thread einfach so freizugeben. Erst seitdem ich Terminate,WaitFor,Free aufrufe, klappt es. Vielleicht ist das ja auch Aberglaube.

Das ist bei Delphi 7 wohl noch so, sollte aber auch durch einen Blick in
Delphi-Quellcode:
TThread.Destroy
zu klären sein, ob das wirklich nötig ist, oder ob es einfach reicht, den echten Destructor abzuwarten und dann erst alles einzureißen (s.o.)

Sir Rufo 25. Jun 2014 18:31

AW: Arbeiten mit TThreadList
 
Hier mal ein Beispiel-Thread, der sich nach außen hin "harmlos" verhält.
Delphi-Quellcode:
TMyForm = class( TForm )
  procedure Button1Click( Sender:TObject );
private
  FMyThread : TMyThread;
public
  procedure AfterConstruction; override;
  procedure BeforeDestruction; override;
end;

procedure TMyThread.Button1Click( Sender:TObject );
var
  LFoo : TFoo;
begin
  // Feed the Thread ...
  LFoo := TFoo.Create;
  try
    FMyThread.WorkOnItem( LFoo );
    LFoo := nil; // Wenn die Instanz vom Thread übernommen wurde, dann hier auf nil setzen
  finally
    LFoo.Free; // stellt bei einer Exception sicher, dass die Instanz freigegeben wird
  end;
end;

procedure TMyForm.AfterConstruction;
begin
  inherited;
  FMyThread :=TMyThread.Create;
end;

procedure TMyForm.BeforeDestruction;
begin
  FreeAndNil( FMyThread );
  // erst wenn die Thread-Instanz wirklich freigegeben werden konnte, dann geht es hier weiter
  inherited;
end;
Delphi-Quellcode:
unit Unit1;

interface

// In älteren Delphi-Versionen hat die Thread-Klasse noch keine TerminatedSet-Methode.
// Dann bitte das nachfolgende {$DEFINE USE_TERMINATEDSET} ausschalten

{$DEFINE USE_TERMINATEDSET }

uses
  Classes, SyncObjs, Contnrs;

type
  TMyThread = class( TThread )
  private
    FCS : TCriticalSection;
    FEvent : TEvent;
    FToDoList : TObjectList;
    procedure DoWorkOnItem( Item : TObject );
    function GetItem : TObject;
  protected
    procedure Execute; override;
{$IFDEF USE_TERMINATEDSET}
    procedure TerminatedSet; override;
{$ENDIF}
  public
    constructor Create;
    destructor Destroy; override;

    // Übergabe eines WorkItems an den Thread.
    // Der Thread übernimmt die Kontrolle über die Item-Instanz
    // und gibt diese bei Bedarf auch wieder frei
    // - Nach dem Abarbeiten
    // - Beim Beenden, wenn noch Items in der Liste enthalten sind

    procedure WorkOnItem( Item : TObject );
  end;

implementation

{ TMyThread }

constructor TMyThread.Create;
begin
  inherited Create( False ); // <-- NEIN, der Thread soll nie, nicht, niemals schlafen

  FCS := TCriticalSection.Create;
  FEvent := TEvent.Create( nil, False, False, '' );
  FToDoList := TObjectList.Create( True );
end;

destructor TMyThread.Destroy;
begin
{$IFDEF USE_TERMINATEDSET}
  // hier einfach nichts machen ... abwarten und Tee trinken
{$ELSE}
  Terminate;
  FEvent.SetEvent;
{$ENDIF}
  inherited;

  // jetzt alle Instanzen freigeben
  FToDoList.Free;
  FEvent.Free;
  FCS.Free;
end;

procedure TMyThread.DoWorkOnItem( Item : TObject );
begin
  // Hier irgendetwas mit dem Item machen
end;

procedure TMyThread.Execute;
var
  LItem : TObject;
begin
  inherited;
  // die übliche Schleife ...
  while not Terminated do
    begin
      // Warten, bis es etwas zu arbeiten gibt
      FEvent.WaitFor;
      // Wenn der Event gefeuert wurde, prüfen wir mal ob ...
      if not Terminated
      then
        begin
          // Item aus der ToDoListe holen
          LItem := GetItem;
          try
            // Mit dem Item arbeiten
            DoWorkOnItem( LItem );
          finally
            // Item-Instanz freigeben
            LItem.Free;
          end;
        end;
    end;
end;

function TMyThread.GetItem : TObject;
begin
  FCS.Enter;
  try
    // Item aus der ToDoListe entnehmen
    Result := FToDoList.Extract( FToDoList.First );
    // Wenn dort nocht Items enthalten sind, dann setzen wir den Event auch wieder
    if FToDoList.Count > 0
    then
      FEvent.SetEvent;
  finally
    FCS.Leave;
  end;
end;

{$IFDEF USE_TERMINATEDSET}

procedure TMyThread.TerminatedSet;
begin
  inherited;
  // Wenn Terminted, dann braucht hier keiner mehr warten
  FEvent.SetEvent;
end;
{$ENDIF}

procedure TMyThread.WorkOnItem( Item : TObject );
begin
  FCS.Enter;
  try
    // Item in die ToDoListe einfügen
    FToDoList.Add( Item );
    // und per Event den Thread aufwecken
    FEvent.SetEvent;
  finally
    FCS.Enter;
  end;
end;

end.
UPDATE Delphi-Referenz durchsuchenTThread.TerminatedSet ist mit XE2 gekommen

Bjoerk 25. Jun 2014 19:01

AW: Arbeiten mit TThreadList
 
Ok. Vielen Dank für dein Beispiel. Die BeforeDestruction werd' ich einbauen.
Delphi-Quellcode:
destructor TD2007Thread.Destroy;
begin
  if (FThreadID <> 0) and not FFinished then
  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;
  RemoveQueuedEvents(Self, nil);
{$IFDEF MSWINDOWS}
  if FHandle <> 0 then CloseHandle(FHandle);
{$ENDIF}
{$IFDEF LINUX}
  // This final check is to ensure that even if the thread was never waited on
  // its resources will be freed.
  if FThreadID <> 0 then pthread_detach(FThreadID);
  sem_destroy(FCreateSuspendedSem);
{$ENDIF}
  inherited Destroy;
  FFatalException.Free;
  RemoveThread;
end;

Sir Rufo 25. Jun 2014 20:42

AW: Arbeiten mit TThreadList
 
Ein Problem sehe ich hier im Destructor
Delphi-Quellcode:
destructor TD2007Thread.Destroy;
begin
  if (FThreadID <> 0) and not FFinished then
  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor; // Diese Stelle ist problematisch
  end;
Das Problem kann hier auftauchen:
Delphi-Quellcode:
var
  LThread : TThread;
begin
  LThread.Create( True );
  LThread.Free;
end;
Denn nun wird zwar im Destructor korrekterweise das Delphi-Referenz durchsuchenTThread.Resume aufgerufen, allerdings dauert es eine geraume Zeit bis der Thread wirklich losläuft und somit auch Delphi-Referenz durchsuchenTThread.WaitFor eine Chance hat darauf zu warten bis der Thread wirklich wieder beendet ist. Hier kann es also passieren, dass nun nicht gewartet wird, der Destructor weiter ausgeführt wird und irgendwann der Thread eigentlich erst losläuft.

In neueren Delphi-Versionen wird genau nach dem
Delphi-Quellcode:
Resume
noch in Kombination mit Delphi-Referenz durchsuchenTThread.Yield (ab XE) gewartet bis der Thread auch wirklich angelaufen ist.
Delphi-Quellcode:
if FCreateSuspended or FSuspended then
  Resume;
while not FStarted do
  Yield;
WaitFor;


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:33 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