Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Einem Thread Zeit geben (https://www.delphipraxis.net/71081-einem-thread-zeit-geben.html)

xaromz 8. Jun 2006 15:01


Einem Thread Zeit geben
 
Hallo,

ich habe ein kleines Thread-Problem.
Ich starte einen Thread, der eine Liste abarbeitet. Während er dies tut, kann es passieren, dass sich die Liste verändert.
Beispielsweise wird die Liste geleert und mit neuen Inhalten gefüllt. Leeren, Füllen und Abarbeiten sind mit CirticalSections geschützt.
Bei jedem Neubefüllen wird ein neuer Thread gestartet.

Mein Problem ist folgendes:
Angenommen, in der Liste sind 100 Elemente.
Der Thread arbeitet sich durch die Liste, und nachdem er bei Position 50 angekommen ist, wird die Liste geleert und mit 100 neuen Elementen gefüllt.
Der Thread bekommt vom Leeren und Neubefüllen nichts mit und macht mit (dem neuen) Element #51 weiter. Würde der Thread mitbekommen, dass die Liste zwischenzeitlich geleert wurde (eine Abfrage ist im Thread vorhanden), dann könnte sich der aktuelle Thread beenden, und der neu erzeugte arbeitet sich durch die neuen Elemente. Leider kommt der Thread aber zwischen Leeren und Neubefüllen nicht zum Zug. Folglich arbeitet der Thread bis zum Element 100, und danach kommt der neue Thread und fängt wieder bei Eins an.

Ich würde also gerne zwischen Leeren und neu Befüllen meinem Thread etwas Zeit abgeben, damit er sich auf die neue Situation einstellen (bzw. sich beenden) kann. Etwa so:
Delphi-Quellcode:
Leere_Liste;
Suspend_MainThread; // <- Hier!
Fülle_Liste;
Nur wie sage ich dem Main-Thread, dass jetzt kurz ein anderer zum Zuge kommen soll?

Gruß
xaromz

shmia 8. Jun 2006 16:29

Re: Einem Thread Zeit geben
 
Zitat:

Zitat von xaromz
Bei jedem Neubefüllen wird ein neuer Thread gestartet.

Warum???
Terminiere doch den alten Thread, ändere die Daten und starte einen Neuen.
Der Thread muss ständig prüfen, ob er nicht schon terminiert wurde:
Delphi-Quellcode:
procedure TXXXThread.Execute;
begin
   for i:=0 to liste.Count-1 do
   begin
      if Termined then Exit; // <<<====
      BearbeiteListenElement(i);  
   end;
end;
Der Thread sollte sich ausserdem beim Hauptthread(Anwendung) melden, wenn er mit der Arbeit fertig ist.

xaromz 8. Jun 2006 17:21

Re: Einem Thread Zeit geben
 
Hallo,
Zitat:

Zitat von shmia
Zitat:

Zitat von xaromz
Bei jedem Neubefüllen wird ein neuer Thread gestartet.

Warum???
Terminiere doch den alten Thread, ändere die Daten und starte einen Neuen.
Der Thread muss ständig prüfen, ob er nicht schon terminiert wurde.
...
Der Thread sollte sich ausserdem beim Hauptthread(Anwendung) melden, wenn er mit der Arbeit fertig ist.

Das Problem ist, das ich einen Fire-and-Forget-Thread benutze, d. h. es gibt im Programm keine Referenz auf den erstellten Thread. Deshalb kann ich den Thread weder terminieren noch kann der Thread eine Rückmeldung liefern (soll er auch gar nicht).
Übrigens weiß die Hauptanwendung gar nicht, dass es da einen Thread gibt :stupid: .

Gruß
xaromz

easywk 8. Jun 2006 17:56

Re: Einem Thread Zeit geben
 
Hi.

Wenn du das leeren und Befüllen eh schon mit einer Semaphore geschützt hast, kann du dir an der Stelle doch einen Merker setzen, dass geändert worden ist. Der Thread setzt diesen Merker zurück, wenn er anfängt zu arbeiten und fragt ihn vor jedem Schleifenzugriff ab. Sitzt der Merker wieder, schießt sich der Thread selber ab ...

Delphi-Quellcode:
procedure TXXXThread.Execute;
begin
   Geaendert:=FALSE;
   for i:=0 to liste.Count-1 do
   begin
      if Geaendert then Exit;
      BearbeiteListenElement(i);  
   end;
end;
cu
easywk

xaromz 8. Jun 2006 18:47

Re: Einem Thread Zeit geben
 
Zitat:

Zitat von easywk
Hi.

Wenn du das leeren und Befüllen eh schon mit einer Semaphore geschützt hast, kann du dir an der Stelle doch einen Merker setzen, dass geändert worden ist. Der Thread setzt diesen Merker zurück, wenn er anfängt zu arbeiten und fragt ihn vor jedem Schleifenzugriff ab. Sitzt der Merker wieder, schießt sich der Thread selber ab ...

Delphi-Quellcode:
procedure TXXXThread.Execute;
begin
   Geaendert:=FALSE;
   for i:=0 to liste.Count-1 do
   begin
      if Geaendert then Exit;
      BearbeiteListenElement(i);  
   end;
end;
cu
easywk

So mach ich das jetzt, aber leider hab' ich da noch ein kleines Problem: Wenn ich das Programm beende, setze ich das Flag einfach präventiv im Destruktor des Objekts, das den Thread erstellt hat, so dass ein eventuell noch laufender Thread sich beenden kann. Leider bekommt aber der Thread wieder keine Chance, auf das Flag zu reagieren, weshalb mir FastMem am Ende ein Speicherleck um die Ohren haut. Damit bin ich wieder bei meinem Originalproblem.

Gruß
xaromz

guste 8. Jun 2006 21:47

Re: Einem Thread Zeit geben
 
Im Prinzip sollte die Abarbeitung so aussehen.


Delphi-Quellcode:
procedure TXXXThread.Execute;
var nIdx: integer;
begin
  nIdx:= 0;
  repeat
    try
      cs.enter;
     
      if liste.Count >= nIdx then
      begin
        machwas mit liste[nidx];
        inc(nIdx);
      end;
      // evtl. nIdx auf 0 setzen wenn liste.count < nIdx
    finally
      cs.leave;
    end;
  until (Idx >= liste.count) or (liste.count = 0) or Application.Terminate;
end;

Olli 8. Jun 2006 23:46

Re: Einem Thread Zeit geben
 
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 :shock:

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.

xaromz 9. Jun 2006 09:47

Re: Einem Thread Zeit geben
 
Hallo,
Zitat:

Zitat von Olli
Aua aua aua ...

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

Mach ich inzwischen. Der Originalcode ist durch Faulheit entstanden (eine Listenänderung kommt nicht so oft vor, deshalb ist normalerweise ah kein Thread aktiv).
Zitat:

Zitat von Olli
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.

Mit dem Zugriff auf die Listenelemente hab ich kein Problem, der ist geschützt.
Zitat:

Zitat von Olli
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):
...
Die farblich gleichgestalteten Befehle entsprechen einander (HLL<>ASM).

Danke, ich spreche fließend x86er assembler :wink: .
Zitat:

Zitat von Olli
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 ...

Ich möchte ja nicht einen anderen Thread schlafenlegen, sondern den Thread, in dem ich mich gerade befinde.
Zitat:

Zitat von Olli
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 ...

Wie gesagt, der Zugriff auf die Elemente ist gesichert.
Zitat:

Zitat von Olli
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 :shock:

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 ...)

Siehe oben.
Zitat:

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

Wenn Du mir Deins leihst :wink: . Aber im Ernst, werd' ich schon noch machen (das Programm ist momentan eh für den Hausgebrauch).
Zitat:

Zitat von Olli
Noch 'ne Idee: Wenn du eine zweifach verkettete Liste nimmst, kann der Thread ein Listenelement ausklinken und später wieder indie 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.

Das wird so nicht funktionieren. Die Änderung der Liste wird nämlich währenddessen angezeigt. In der Liste befinden sich Objekte mit den Dateinamen von Mediendateien (mp3, avi, mpeg...). Die Namen stehen in einer Listbox. Im Hintergrund werden diese Objekte mit Metadaten der Medien gefüllt (Bitrate, Videoauflösung, Spurenanzahl, ID3-Tags...), und gegebenenfalls die Anzeige in der Listbox verändert.
Da ich eine bestimmte Reihenfolge in der Liste habe und die Liste auch immer vollstängig sein muss, funktioniert das so leider nicht.

Mein Problem hat sich aber jetzt verlagert (Siehe Posting #5):
Wenn ich das Programm beende, während der Thread noch läuft, erhält dieser keine Chance, sich sauber zu beenden. Deshalb möchte ich eben beim Beenden des Programms den Hauptthread kurz schlafen legen, damit der Arbeitsthread auf ein Flag reagieren und sich beenden kann.
Ich möchte also Windows sagen, dass jetzt erst mal alle anderen Threads ausgeführt werden sollen (meinetwegen auch ein bestimmter Thread, ich hab das Handle ja), und danach wieder der Hauptthread.

Gruß
xaromz

Olli 9. Jun 2006 10:44

Re: Einem Thread Zeit geben
 
Zitat:

Zitat von xaromz
Mach ich inzwischen. Der Originalcode ist durch Faulheit entstanden (eine Listenänderung kommt nicht so oft vor, deshalb ist normalerweise ah kein Thread aktiv).

Gut! :zwinker:

Zitat:

Zitat von xaromz
Mit dem Zugriff auf die Listenelemente hab ich kein Problem, der ist geschützt.

Wie? Ansonsten gälte u.U. das hier: 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 ...

Zitat:

Zitat von xaromz
Danke, ich spreche fließend x86er assembler :wink: .

Ich hoffe nicht, weil ich mich dann quasi blamiert habe - da steht ja aus Assemblersicht nur Nonsens, auch wenn die Opcodes selber existieren :mrgreen:

Zitat:

Zitat von xaromz
Wie gesagt, der Zugriff auf die Elemente ist gesichert.

Wie, wie, wie? Wenn du ein Ausschlußobjekt für eine riesige Liste hast, kann nur einer lesen oder schreiben, also serialisierst du den Zugriff wieder künstlich.

Zitat:

Zitat von xaromz
Siehe oben.

Hat leider mit dem geschützten Zugriff nichts zu tun, wenn du auf die "Gleichzeitigkeit" der Threads wertlegst.

Zitat:

Zitat von xaromz
Das wird so nicht funktionieren. Die Änderung der Liste wird nämlich währenddessen angezeigt. In der Liste befinden sich Objekte mit den Dateinamen von Mediendateien (mp3, avi, mpeg...). Die Namen stehen in einer Listbox. Im Hintergrund werden diese Objekte mit Metadaten der Medien gefüllt (Bitrate, Videoauflösung, Spurenanzahl, ID3-Tags...), und gegebenenfalls die Anzeige in der Listbox verändert.
Da ich eine bestimmte Reihenfolge in der Liste habe und die Liste auch immer vollstängig sein muss, funktioniert das so leider nicht.

Dann stellt sich mir aber die Frage, warum du die Liste immer neu befüllst anstatt den jeweiligen Eintrag zu ergänzen (oder zu ersetzen). Eine doppelt verlinkte Liste ermöglicht dir das einfache Einfügen oder Entfernen von Listenelementen ohne gleich die komplette Liste neu befüllen zu müssen ...

Zitat:

Zitat von xaromz
Wenn ich das Programm beende, während der Thread noch läuft, erhält dieser keine Chance, sich sauber zu beenden. Deshalb möchte ich eben beim Beenden des Programms den Hauptthread kurz schlafen legen, damit der Arbeitsthread auf ein Flag reagieren und sich beenden kann.

und wie wacht der Hauptthread dann wieder von alleine auf? Dann wird dein Programm nämlich vermutlich zwangsbeendigt.

Meines Erachtens ist deine Denk- und Herangehensweise zu naiv. Du könntest dir eine Menge Arbeit sparen, wenn jede Liste ein Ausschlußobjekt hat (könnte ein Byte- oder DWORD-Wert sein), der immer nur über die Interlocked-Funktionen bearbeitet wird. Dieses Ausschlußobjekt ist kein echtes Objekt sondern ein Referenzzähler. Da jedes Objekt nun nur noch ein Pointer auf einen Speicherbereich ist, übergibst du dieses an einen Thread nachdem der Aufrufer den Referenzzähler um 1 erhöht hat. Danach kann der Thread erstmal machen was er will, außer daß er davon ausgehen muß, daß die Daten jederzeit von einem anderen Thread gelesen werden können - die Integrität muß also garantiert sein (z.B. kritischer Abschnitt für jede der zu schreibenden Informationen ...). Wenn der (Arbeits-)Thread mit der Abarbeitung fertig ist, legt er sich erstmal selber schlafen (suspend) nachdem er den Referenzzähler um 1 reduziert hat. Dadurch ist er immer an einer wohldefinierten Stelle eingeschlafen. Will der Dispatcher-Thread nun dem Arbeits-Thread sagen, daß er wieder was machen soll, wird der Pointer (globale Variable o.ä.) zum Listeneintrag atomisch ausgetauscht und der Arbeits-Thread wieder aufgeweckt, womit die Schleife im Arbeits-Thread ihre Arbeit weiterführt. Damit ist zwar ein extra Thread (der Dispatcher) nötig, aber die Anzahl der Arbeits-Threads kann ohne Mühe variiert werden. Außerdem mußt du nur einen Listeneintrag in der Listbox aktualisieren, nicht die komplette Liste.

Zitat:

Zitat von xaromz
Ich möchte also Windows sagen, dass jetzt erst mal alle anderen Threads ausgeführt werden sollen (meinetwegen auch ein bestimmter Thread, ich hab das Handle ja), und danach wieder der Hauptthread.

Du kannst aber nur Windows sagen, daß dein aktueller Thread seine Rechenzeit abgibt ... an wen, läßt sich der Scheduler von einem Usermode-Programm sicher nicht vorschreiben ;) ... selbst im Kernelmode geht das nur beschränkt.

xaromz 9. Jun 2006 11:47

Re: Einem Thread Zeit geben
 
Hallo,
Zitat:

Zitat von Olli
Zitat:

Zitat von xaromz
Mit dem Zugriff auf die Listenelemente hab ich kein Problem, der ist geschützt.

Wie? Ansonsten gälte u.U. das hier: 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 ...

Ich verwende eine CriticalSection, wenn ich die Liste irgendwo bearbeite. Das klappt auch so, wie es soll.
Zitat:

Zitat von Olli
Zitat:

Zitat von xaromz
Danke, ich spreche fließend x86er assembler :wink: .

Ich hoffe nicht, weil ich mich dann quasi blamiert habe - da steht ja aus Assemblersicht nur Nonsens, auch wenn die Opcodes selber existieren :mrgreen:

:mrgreen:
Zitat:

Zitat von Olli
Zitat:

Zitat von xaromz
Wie gesagt, der Zugriff auf die Elemente ist gesichert.

Wie, wie, wie? Wenn du ein Ausschlußobjekt für eine riesige Liste hast, kann nur einer lesen oder schreiben, also serialisierst du den Zugriff wieder künstlich.

Natürlich muss ich den Zugriff irgendwie serialisieren. Da ich (s. o.) mit CriticalSection arbeite und damit sehr sparsam umgehe, serialisiere ich nur dort, wo es unumgänglich ist (Schreibzugriff auf Listenobjekte, Änderung der Liste). Ich ziehe also dem Arbeitsthread kein Listenelement unterm Arsch weg :wink: .
Zitat:

Zitat von Olli
Zitat:

Zitat von xaromz
Siehe oben.

Hat leider mit dem geschützten Zugriff nichts zu tun, wenn du auf die "Gleichzeitigkeit" der Threads wertlegst.

Wie gesagt, die Gleichzeitigkeit funktioniert prima.

Zitat:

Zitat von Olli
Dann stellt sich mir aber die Frage, warum du die Liste immer neu befüllst anstatt den jeweiligen Eintrag zu ergänzen (oder zu ersetzen). Eine doppelt verlinkte Liste ermöglicht dir das einfache Einfügen oder Entfernen von Listenelementen ohne gleich die komplette Liste neu befüllen zu müssen ...

Nun, wenn in der Liste irgendwas drinsteht und der User einfach komplett andere Medien lädt, was bleibt mir anderes übrig, als den kompletten Listeninhalt zu ersetzen?
Zitat:

Zitat von Olli
Zitat:

Zitat von xaromz
Wenn ich das Programm beende, während der Thread noch läuft, erhält dieser keine Chance, sich sauber zu beenden. Deshalb möchte ich eben beim Beenden des Programms den Hauptthread kurz schlafen legen, damit der Arbeitsthread auf ein Flag reagieren und sich beenden kann.

und wie wacht der Hauptthread dann wieder von alleine auf? Dann wird dein Programm nämlich vermutlich zwangsbeendigt.

Sowas habe ich schon befürchtet.
Zitat:

Zitat von Olli
Meines Erachtens ist deine Denk- und Herangehensweise zu naiv. Du könntest dir eine Menge Arbeit sparen, wenn jede Liste ein Ausschlußobjekt hat (könnte ein Byte- oder DWORD-Wert sein), der immer nur über die Interlocked-Funktionen bearbeitet wird. Dieses Ausschlußobjekt ist kein echtes Objekt sondern ein Referenzzähler. Da jedes Objekt nun nur noch ein Pointer auf einen Speicherbereich ist, übergibst du dieses an einen Thread nachdem der Aufrufer den Referenzzähler um 1 erhöht hat. Danach kann der Thread erstmal machen was er will, außer daß er davon ausgehen muß, daß die Daten jederzeit von einem anderen Thread gelesen werden können - die Integrität muß also garantiert sein (z.B. kritischer Abschnitt für jede der zu schreibenden Informationen ...). Wenn der (Arbeits-)Thread mit der Abarbeitung fertig ist, legt er sich erstmal selber schlafen (suspend) nachdem er den Referenzzähler um 1 reduziert hat. Dadurch ist er immer an einer wohldefinierten Stelle eingeschlafen. Will der Dispatcher-Thread nun dem Arbeits-Thread sagen, daß er wieder was machen soll, wird der Pointer (globale Variable o.ä.) zum Listeneintrag atomisch ausgetauscht und der Arbeits-Thread wieder aufgeweckt, womit die Schleife im Arbeits-Thread ihre Arbeit weiterführt. Damit ist zwar ein extra Thread (der Dispatcher) nötig, aber die Anzahl der Arbeits-Threads kann ohne Mühe variiert werden. Außerdem mußt du nur einen Listeneintrag in der Listbox aktualisieren, nicht die komplette Liste.

Ist zwar rührend, wie Du Dich um meine Threads kümmerst, aber das ist ja nicht mein Problem :wink: . Normalerweise funktioniert das prächtig.
Egal, wie ich den Zugrif steuere (Criticl Section, Interlock-Referenzzähler, Semaphore...), wenn ein Thread läuft, während ich das Programm beende, dann läuft er erstmal weiter und wird dann vermutlich einfach "abgeschossen".

Aber ich hab' das gerade so gelöst:
- Falls beim Beenden ein Thread läuft, signalisiere ich dem Thread ein Terminate und rufe WaitFor auf
- Anschließend werte ich das Flag im Thread aus und setze FreeOnTerminate aus False, damit das Handle in WaitFor erhalten bleibt und keine AV kommt
- Sobald WaitFor zurückgekehrt ist, zerstöre ich den Thread mit Free
Zitat:

Zitat von Olli
Du kannst aber nur Windows sagen, daß dein aktueller Thread seine Rechenzeit abgibt ... an wen, läßt sich der Scheduler von einem Usermode-Programm sicher nicht vorschreiben ;) ... selbst im Kernelmode geht das nur beschränkt.

Sowas hab' ich mir schon gedacht.

Aber mein Problem ist ja jetzt gelöst.

Gruß
xaromz


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:47 Uhr.
Seite 1 von 2  1 2      

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