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
SuspendThread 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