AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Arbeiten mit TThreadList

Ein Thema von Captnemo · begonnen am 7. Aug 2013 · letzter Beitrag vom 25. Jun 2014
Antwort Antwort
Seite 3 von 3     123   
Der schöne Günther

Registriert seit: 6. Mär 2013
6.156 Beiträge
 
Delphi 10 Seattle Enterprise
 
#21

AW: Arbeiten mit TThreadList

  Alt 23. Jun 2014, 18:35
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 TMyThread = class(TThread) zu definieren, Execute() zu implementieren und den letztendlich zurückzugeben.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#22

AW: Arbeiten mit TThreadList

  Alt 23. Jun 2014, 20:01
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 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 TThread.WaitFor .

Fazit: Die Thread-Klasse sauber aufbauen, dann klappt das wie von selber
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#23

AW: Arbeiten mit TThreadList

  Alt 25. Jun 2014, 11:42
Ein Thread ruft im Destroy ganz von alleine das Delphi-Referenz durchsuchenTThread.Terminate auf, dann ein 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.
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#24

AW: Arbeiten mit TThreadList

  Alt 25. Jun 2014, 11:55
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;
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#25

AW: Arbeiten mit TThreadList

  Alt 25. Jun 2014, 18:50
Es sollte reichen, wenn du die Instanzen nach dem 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 TThread.Destroy so passiert.

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

Ein Thread ruft im Destroy ganz von alleine das Delphi-Referenz durchsuchenTThread.Terminate auf, dann ein 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 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.)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#26

AW: Arbeiten mit TThreadList

  Alt 25. Jun 2014, 19:31
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
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo (25. Jun 2014 um 21:47 Uhr)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#27

AW: Arbeiten mit TThreadList

  Alt 25. Jun 2014, 20:01
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;
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#28

AW: Arbeiten mit TThreadList

  Alt 25. Jun 2014, 21:42
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 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;
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo (25. Jun 2014 um 21:52 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 3     123   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:53 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