Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Best Practice : ein Object in enem anderen Thread abfragen (https://www.delphipraxis.net/199471-best-practice-ein-object-enem-anderen-thread-abfragen.html)

MyRealName 25. Jan 2019 14:20

Best Practice : ein Object in enem anderen Thread abfragen
 
Ich habe in einem Service mehrere Datenbank threads (jeder kümmert sich um ene andere DB) und wenn ich diesen service runterfahren will, dann will ich sicherstellen, dass ich nicht in einem der threads gerade was tue. Ich weiss, dass Schreiben auf eine Variable in einem anderen Thread ganz sicher Probleme bringen kann, ist das mit Lesen genauso? Könnte ich dem Datanmodule, welches in einem anderen Thread nur genutzt wird, eine public property "Busy" verpassen, welche von ausserhalb gelesen werden kann ?

Wenn nicht, was wäre die beste (nicht asynchrone) Methode, um das zu erreichen.

Die Idee war sowas wie

Code:

While List.count > 0 do begin
  for item in List do
  begin
    If not Item.Busy then
    begin
      List.Remove(Item);
      Item.free;
    end
  end;
  If List.Count > 0
    Delay(100);
end;
Danke schonmal

peterbelow 25. Jan 2019 14:34

AW: Best Practice : ein Object in enem anderen Thread abfragen
 
Lesen ist normalerweise problemlos möglich, wenn die entsprechende Variable byte oder boolean ist, da sind operationen auf jeden Fall "atomisch" (können von einem Thread-switch nicht unterbrochen werden). Bei Variablen, die 2 oder 4 bytes belegen sind Operationen nur atomisch, wenn die Addresse der Variablen entsprechend aligned ist (d.h. eine Potenz von 2 bzw. 4). Bei Feldern in einem Objekt oder globalen Variablen sorgt der Compiler für eine entsprechendes Alignment.

In deinem Fall sollte also ein Busy flag (Boolean) keine Probleme machen, solange Dir nicht irgendein Code das Datenmodul selbst unter dem Hintern wegschießt :).

Allerdings gibt es da immer noch Fallen. Wenn Du ein Flag aus Thread A liest, welches von Thread B gesetzt oder gelöscht werden kann, kann der gelesene Wert schon veraltet sein, bevor Thread A ihn verarbeiten kann. Ob das ein Problem sein kann hängt von den Details deines Scenarios ab.

Normalerweise geht man wie folgt vor:

Thread B (der Hintergrundthread) hat eine Arbeitsschleife, die bei jeder Runde prüft, ob die Terminated property des Threads true ist. Wenn ja wie Schleife verlassen und der Thread beendet sich. Thread A kann dann ThreadB.Terminate aufrufen, um das Flag zu setzen, und dann ThreadB.Waitfor, um zu warten, bis der Thread sich beendet hat. Man sollte aber auf jeden Fall einen sinnvollen Timeout an Waitfor übergeben, sonst wartet ThreadA eventuell ewig, wenn sich B aufgehängt hat.

MyRealName 25. Jan 2019 14:49

AW: Best Practice : ein Object in enem anderen Thread abfragen
 
Das mit dem terminate muss ich mal schauen, weil ich nutze zum Threading die RealThinClient Komponente TRtcQuickJob, da kann ich zwar nachsehen, ob jobs laufen, aber ich kann sie wohl nicht einfach unterbrechen und zwischen 2 jobs sagen : Mach mal nicht weiter, ich will den service runterfahren. Rein technisch kann ich aber sicher eine Nachricht an den thread message handler schicken. Mal basteln.

Danke Peter

Rollo62 25. Jan 2019 15:35

AW: Best Practice : ein Object in enem anderen Thread abfragen
 
Ich würde ein BusyFlag als Integer spendieren, und mit
Delphi-Quellcode:
function GetBusy : Boolean;
begin
  LRes  := TInterlocked.Exchange( LThreadFlag, LThreadFlag);
  Result := LRes <> 0;
end;
abfragen, sicherheitshalber.
Was Peter schreibt stimmt schon, aber weil die Busy-Abfrage ja nicht permanent läuft würde ich mir da etwas mehr "Freiraum" drumrum schaffen,
damit nicht bei plötzlichen Änderungen des Typs aus Versehen etwas in die Hose geht.

MyRealName 25. Jan 2019 17:24

AW: Best Practice : ein Object in enem anderen Thread abfragen
 
Zitat:

Zitat von Rollo62 (Beitrag 1424144)
Ich würde ein BusyFlag als Integer spendieren, und mit
Delphi-Quellcode:
function GetBusy : Boolean;
begin
  LRes  := TInterlocked.Exchange( LThreadFlag, LThreadFlag);
  Result := LRes <> 0;
end;
abfragen, sicherheitshalber.
Was Peter schreibt stimmt schon, aber weil die Busy-Abfrage ja nicht permanent läuft würde ich mir da etwas mehr "Freiraum" drumrum schaffen,
damit nicht bei plötzlichen Änderungen des Typs aus Versehen etwas in die Hose geht.

Das mit dem Exhange geht nicht, da es keine Object properties annimmt und da ich mehrere Instanzen des Objects habe, kann ich keine globale variable nehmen.
Im moment mache ich das setzen von aussen mit critical sections, das lesen ohne. Von innen (im thread) wird es nur gelesen, nur beim erzeugen einmal auf False gesetzt.

Rollo62 25. Jan 2019 17:33

AW: Best Practice : ein Object in enem anderen Thread abfragen
 
Ich meinte ja auch ein Flag pro Objektinstanz, um das Busy der jeweiligen
Instanz zu entkoppeln.
So hatte ich dich verstanden, das du jede Thread-Instanz einzeln abfragen möchtest.

Delphi-Quellcode:
function TDeinThreadObject.GetBusy : Boolean;
begin
..

Schokohase 26. Jan 2019 08:16

AW: Best Practice : ein Object in enem anderen Thread abfragen
 
Wenn du möchtest, dass ein Thread seine Arbeit beendet, dann rufe die Methode Delphi-Referenz durchsuchenTThread.Terminate auf.

Innerhalb des Threads kannst du die Eigenschaft Delphi-Referenz durchsuchenTThread.Terminated (
Delphi-Quellcode:
protected
) abfragen, ob es da einen Wunsch nach Beendigung gibt.

Zusätzlich gibt es auch die Methode Delphi-Referenz durchsuchenTThread.TerminatedSet die man überschreiben kann. Diese wird beim erstmaligen Aufruf von Delphi-Referenz durchsuchenTThread.Terminate aufgerufen.

Grundsätzlich entscheidet der Thread immer selber ob er sich nun beendigt oder nicht, denn nur der Thread selber weiß um seinen Zustand und wann dieser gefahrlos verlassen werden kann.

Eine simple Execute-Methode in einem Thread sieht z.B. so aus:
Delphi-Quellcode:
procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    // mach was simuliertes
    Sleep(10);
  end;
end;
Der läuft jetzt so lange, bis man Delphi-Referenz durchsuchenTThread.Terminate aufruft (was im Übrigen von Delphi-Referenz durchsuchenTThread.Destroy intern auch aufgerufen wird).


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz