AGB  ·  Datenschutz  ·  Impressum  







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

Einem Thread Zeit geben

Ein Thema von xaromz · begonnen am 8. Jun 2006 · letzter Beitrag vom 9. Jun 2006
Antwort Antwort
Seite 1 von 2  1 2      
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#1

Einem Thread Zeit geben

  Alt 8. Jun 2006, 16:01
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
  Mit Zitat antworten Zitat
shmia

Registriert seit: 2. Mär 2004
5.508 Beiträge
 
Delphi 5 Professional
 
#2

Re: Einem Thread Zeit geben

  Alt 8. Jun 2006, 17:29
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.
Andreas
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#3

Re: Einem Thread Zeit geben

  Alt 8. Jun 2006, 18:21
Hallo,
Zitat von shmia:
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 .

Gruß
xaromz
  Mit Zitat antworten Zitat
easywk

Registriert seit: 9. Jul 2003
Ort: Schwanewede
117 Beiträge
 
Delphi 7 Enterprise
 
#4

Re: Einem Thread Zeit geben

  Alt 8. Jun 2006, 18:56
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
Björn
if all else fails - read the instructions
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#5

Re: Einem Thread Zeit geben

  Alt 8. Jun 2006, 19:47
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
  Mit Zitat antworten Zitat
guste

Registriert seit: 23. Apr 2006
19 Beiträge
 
#6

Re: Einem Thread Zeit geben

  Alt 8. Jun 2006, 22:47
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;
guste
  Mit Zitat antworten Zitat
Olli
(Gast)

n/a Beiträge
 
#7

Re: Einem Thread Zeit geben

  Alt 9. Jun 2006, 00: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
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#8

Re: Einem Thread Zeit geben

  Alt 9. Jun 2006, 10:47
Hallo,
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 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 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 .
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 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 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

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 von Olli:
Tip: Teste dein Prorgramm auch auf einem SMP-System!!!
Wenn Du mir Deins leihst . Aber im Ernst, werd' ich schon noch machen (das Programm ist momentan eh für den Hausgebrauch).
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
  Mit Zitat antworten Zitat
Olli
(Gast)

n/a Beiträge
 
#9

Re: Einem Thread Zeit geben

  Alt 9. Jun 2006, 11:44
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!

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 von xaromz:
Danke, ich spreche fließend x86er assembler .
Ich hoffe nicht, weil ich mich dann quasi blamiert habe - da steht ja aus Assemblersicht nur Nonsens, auch wenn die Opcodes selber existieren

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 von xaromz:
Siehe oben.
Hat leider mit dem geschützten Zugriff nichts zu tun, wenn du auf die "Gleichzeitigkeit" der Threads wertlegst.

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 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 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.
  Mit Zitat antworten Zitat
xaromz

Registriert seit: 18. Mär 2005
1.682 Beiträge
 
Delphi 2006 Enterprise
 
#10

Re: Einem Thread Zeit geben

  Alt 9. Jun 2006, 12:47
Hallo,
Zitat von Olli:
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 von Olli:
Zitat von xaromz:
Danke, ich spreche fließend x86er assembler .
Ich hoffe nicht, weil ich mich dann quasi blamiert habe - da steht ja aus Assemblersicht nur Nonsens, auch wenn die Opcodes selber existieren

Zitat von Olli:
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 .
Zitat von Olli:
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 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 von Olli:
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 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 . 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 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
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 18:47 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