Thema: Delphi Einem Thread Zeit geben

Einzelnen Beitrag anzeigen

Olli
(Gast)

n/a Beiträge
 
#7

Re: Einem Thread Zeit geben

  Alt 8. Jun 2006, 23:46
Aua aua aua ...

Von der ständigen Neuerstellung eines Threads kann ich nur abraten. Man sollte den einen Thread bei wiederkehrenden Aufgaben immer wiederverwenden.

Und dann gehen wir nochmal ein wenig in's Detail in Sachen Threads. Von außen kann man den Thread niemals sicher an einer bestimmten Stelle anhalten [s.u.]. Das sinnvollste wäre also eine Kombination aus einer Liste/Listenklasse welche sich bewußt ist, daß mehrere Threads darauf zugreifen. Ich würde in jedem Fall eine verkettete Liste benutzen. Der Hauptthread sollte sich mit MSDN-Library durchsuchenSuspendThread selbst schlafenlegen und nach dem Aufwachen checken ob die Liste noch gültig ist. Sinnvoll wäre da vermutlich so eine Art Ringpuffer aus mindestens 2 LIST_ENTRY Strukturen, wobei sich der Thread merkt auf welche Liste (0 oder 1) er gerade zugegriffen hat und dann nach dem Aufwachen erstmal testet, ob die Liste noch die gültige ist. Den Index der gültigen Liste würde ich threadsicher über die Interlocked*-Funktionen jeweils ändern und auch auslesen. Achtung, jede Liste braucht auch ein Ausschlußobjekt (Semaphore, Mutex oder Critical Section o.ä.). Das Ausschlußobjekt garantiert, daß die Liste solange weiterlebt, bis der (nun wieder aufgewachte Thread) es freigegeben hat.

Sinnvoll wäre desweiteren, wenn der Thread in einer globalen Variablen (oder Member-Variablen) über die Interlocked-Funktion auch den Index des aktuell bearbeiteten Elements festhalten würde. Dann könnte der Thread welcher die Listen befüllt und auch wieder freigibt schonmal alle Elemente bis auf das fragliche wieder freigeben und würde zuguterletzt nur darauf warten dieses letzte Element freizugeben sobald das Ausschlußobjekt wieder frei ist.

Nun nochmal zum Anhalten

Wenn wir uns vor Augen führen, daß so ziemlich jeder "Delphi-Befehl" in viele (nicht-atomare) Opcodes (also CPU-Befehle) zerlegt wird, dann kann man sich leicht vorstellen, daß wir in "Delphi" folgendes haben (EIP = aktuelle Verarbeitungsstelle des Threads):

Code:
[color=red]Bla;[/color]
[color=green]Bla2(ListenElement);[/color]
[color=blue]Bla3;[/color]
in "Assembler" sähe das dann z.B. so aus:
Code:
[color=red]XOR ...
MOV ...
OR ...
MOV ...
XCHG ...
MOV ...
AND ...
TEST ...
XOR ...
MOV ...
OR ...[/color]
[color=green]MOV ...
XCHG ...
MOV ...
AND ...
TEST ...
XOR ...
MOV ...
OR ...
MOV ...
[u][b]XCHG ...[/b][/u][EIP]
MOV ...
AND ...
TEST ...
OR ...
MOV ...
XCHG ...
MOV ...
AND ...
TEST ...
XOR ...
MOV ...
XOR ...[/color]
[color=blue]MOV ...
OR ...
MOV ...
XCHG ...
MOV ...
AND ...
TEST ...
XOR ...
MOV ...
OR ...
MOV ...
XCHG ...
MOV ...
AND ...
TEST ...[/color]
Die farblich gleichgestalteten Befehle entsprechen einander (HLL<>ASM).

Aber holla, dadurch daß wir den Thread von außerhalb schlafengelegt haben, steht die Ausführung mitten in der Funktion die auf das Listenelement zugreift. Wird das Listenelement also freigegeben, kann dennoch ein Zugriff auf den entsprechenden (nun freigegebenen) Speicherbereich passieren. Mit etwas Glück kommt nur Müll raus, mit etwas Pech schießt sich der Thread selber ab, mit noch mehr Pech landet die Anwendung in den ewigen Jagd...ähem...Ausführungsgründen ...

Das was guste geschrieben hat, ist einerseits nicht so dumm, andererseits naiv. Das Problem ist, daß man jedem Listenelement eine eigene Critical Section mitgeben müßte. Tut man's nicht, hat man sogleich den Sinn des Thread ad absurdum geführt ...




Anwendung:
Nehmen wir die Beispiele von oben, kackt dir der Thread in den meisten Fällen auch ab, wenn EIP mitten in der grün gefärbten Funktion stehenbleibt

Code:
procedure TXXXThread.Execute;
begin
   Geaendert:=FALSE;
   for i:=0 to liste.Count-1 do
   begin
      if Geaendert then Exit;
      [color=green]BearbeiteListenElement(i);[/color]
   end;
end;
(Geht bei shmia's Beispiel auch ...)



Tip: Teste dein Prorgramm auch auf einem SMP-System!!!


Noch 'ne Idee: Wenn du eine zweifach verkettete Liste nimmst, kann der Thread ein Listenelement ausklinken und später wieder in die Liste einklinken. Wenn du dann immer mit Interlocked-Funktionen arbeitest, kann der "Füll-Thread" das gleiche machen und dein aktuell bearbeitetes Listenelement an den Anfang der Liste stellen. Kommt aber eben drauf an, ob deine Aufgabe zyklisch oder linear ist.
  Mit Zitat antworten Zitat