Discussion:
Multi-Thread und Sleep
(zu alt für eine Antwort)
Christian Bode
2008-05-04 15:07:52 UTC
Permalink
Hallo,

Die Verwendung der Methode Sleep ist mir noch nicht ganz klar. Ich habe
jeweils zwei zusätzliche Threads, beide werden mit der Methode Synchronize
aufgerufen. Innerhalb dieser Threads wird zeitweilig die Methode Wait
aufgerufen, welche mit AppProcMsg arbeitet. Da die Threads synchronized
sind, dürfte das okay sein.

procedure Wait(Milliseconds: Cardinal);
var
StopTime: Cardinal;
begin
StopTime := GetTickCount + Milliseconds;
{ Abbruch der Andwendung wird ebenfalls berücksichtigt }
repeat
Application.ProcessMessages;
Sleep(1);
until (GetTickCount >= StopTime) or Application.Terminated;
end;

Meistens funktioniert das auch alles ganz gut, nur manchmal habe ich den
Effekt, das einer meiner Threads erst arbeitet, wenn die Wait-Methode
verlassen wurde, obwohl sie in einem völlig anderem Thread aufgerufen wird.
Manchmal wiederum läuft aber alles parallel. Außerdem benötige ich noch eine
manuelle Abbruchmöglichkeit, um den Thread auch unabhängig des Wait-Timeouts
zu stoppen. Gibt es vielleicht außerdem dem Sleep noch eine ellegantere Art
einen Thread zu pausieren? z.B. über Events und WaitForSingleObjects oder
so?

Grüße
Christian
Arno Garrels
2008-05-04 18:34:17 UTC
Permalink
Post by Christian Bode
Die Verwendung der Methode Sleep ist mir noch nicht ganz klar.
Sleep hält denjenigen Thread, in dem Sleep aufgerufen wird an.
Alle anderen Threads arbeiten ganz normal weiter, das
Application.ProcessMessages ist hier also Unsinn.
Post by Christian Bode
Ich
habe jeweils zwei zusätzliche Threads, beide werden mit der Methode
Synchronize aufgerufen.
Verstehe ich nicht.
Post by Christian Bode
Gibt es
vielleicht außerdem dem Sleep noch eine ellegantere Art einen Thread
zu pausieren? z.B. über Events und WaitForSingleObjects oder so?
Ja, das wurde hier vor ein paar Tagen durchgekaut, entweder
vier Threads unterhalb deinem nachlesen, oder hier:
http://groups.google.com/group/de.comp.lang.delphi.misc/browse_thread/thread/27b004c777467f30/

Eine Alternative zu Heiko's Lösung wäre mit Messages zu arbeiten.
Dazu benötigst Du eine MessagePump in der Methode Execute.
Die wäre eh erforderlich, wenn z.B. Komponenten, die im Thread laufen
auf Message-Verarbeitung angewiesen wären. Dann schickst du dem Thread
mit PostThreadMessage() eigene Botschaften, die ihn anweist etwas zu
tun. Ein Vorteil der Messages ist, dass man zwei Parameter mitgeben
kann.

--
Arno Garrels
Christian Bode
2008-05-05 19:44:55 UTC
Permalink
Post by Arno Garrels
Sleep hält denjenigen Thread, in dem Sleep aufgerufen wird an.
In meinem Fall leider nicht nur den Thread in welchem Sleep aufgerufen wird.
Post by Arno Garrels
Application.ProcessMessages ist hier also Unsinn.
Nicht unbedingt, es zeigt bei mir schon eine unterschiedliche Wirkungsweise.
Mit dessen Aufruf bleibt das Programm bedienbar und ich kann es Beenden wenn
ich möchte. Ohne Aufruf ist es für die Zeit des Sleeps blockiert.
Post by Arno Garrels
Post by Christian Bode
habe jeweils zwei zusätzliche Threads, beide werden mit der Methode
Synchronize aufgerufen.
Verstehe ich nicht.
Es läuft der VCL-Thread und zusätzlich ein MainThread und ein EventThread.
Im EventThread wird hier und da Sleep aufgerufen. Allerdings blockiert
dieses ohne Aufruf von AppProMsgs auch die Verarbeitung anderer Dinge. Aber
selbst mit dessen Aufruf klappts nicht immer 100%.
Ich vermute stark das hängt mit dem Synchronize zusammen. Was ich meinte
war, das der Inhalt des Executes der beiden Threads jeweils mit Synchronize
aufgerufen wird und damit mit dem VCL-Thread abgeglichen wird. Kann es sein,
das dadurch kein echtes Multi-Threading ensteht, da er ja jeder Thread nur
zeitweise laufen lässt. Liegt es vielleicht am Synchronize das mein Sleep
den Rest des Programmes blockiert?
Post by Arno Garrels
Ja, das wurde hier vor ein paar Tagen durchgekaut, entweder vier Threads
http://groups.google.com/group/de.comp.lang.delphi.misc/browse_thread/thread/27b004c777467f30/
Auf dieses werde ich vielleicht sogar zurückgreifen ... zunächst möchte ich
jedoch rausfinden, wo derzeit das Problem in meinem Code liegt.
Ich hab festgestellt, das die Threads auch unterschiedlich anlaufen. Wenn
z.B. als erstes der EventThread gestartet wird und dieser ein Sleep(10000)
ausführt, wird der MainThread erst verarbeitet, wenn die 10 Sekunden vorbei
sind.
Wenn jedoch erst der MainThread gestartet wird und innerhalb der
überschriebenen Sleep Methode ein ApplicationProcessMessages ausgeführt
wird, wartet der EventThread, aber der MainThread läuft weiter.
Christian Gudrian
2008-05-06 06:03:18 UTC
Permalink
Post by Christian Bode
Mit dessen Aufruf bleibt das Programm bedienbar und ich kann es Beenden wenn
ich möchte. Ohne Aufruf ist es für die Zeit des Sleeps blockiert.
Dann hältst Du mit Sleep den Haupt-Thread an, in dem auch die
Nachrichtenschleife der VCL läuft.

Christian
Christian Bode
2008-05-05 20:11:04 UTC
Permalink
Post by Christian Bode
zu pausieren? z.B. über Events und WaitForSingleObjects oder so?
Ich habe meine Wait-Funktion jetzt mal umgestellt und arbeite mit einem
Event und WaitForSingleObjects mit Timeout. Leider hat das den gleichen
Effekt wie mein zuvor verwendetes Sleep. Es blockiert nicht nur den einen
Thread, sondern das gesamte Programm.
Ich gehe irgendwie stark davon aus, es hängt mit der Synchronisierung der
Threads zusammen. Dummerweise komme ich ohne die auch nicht so richtig aus,
da beide Threads Aufrufe verwenden die nicht Thread-Safe sind.
Kann jemand bestätigen, das bei der Synchronisierung der Threads ein Sleep
sich auch auf den Rest der Anwendung auswirkt oder liegt vielleicht noch
irgendwo ein Fehler vor?

Danke
Christian
Marian Aldenhövel
2008-05-06 06:51:20 UTC
Permalink
Hallo,
Post by Christian Bode
Kann jemand bestätigen, das bei der Synchronisierung der Threads ein Sleep
sich auch auf den Rest der Anwendung auswirkt oder liegt vielleicht noch
irgendwo ein Fehler vor?
Offenbar liegt ein Fehler vor.

Sleep() legt nur den Thread schlafen, in dessen Kontext es aufgerufen wird.

Also verklemmt sich bei Dir etwas anderes.

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Christian Bode
2008-05-06 07:28:35 UTC
Permalink
Hallo Marian,
Post by Marian Aldenhövel
Sleep() legt nur den Thread schlafen, in dessen Kontext es aufgerufen wird.
Also verklemmt sich bei Dir etwas anderes.
Also der Ablauf ist nicht der:

procedure TMyThread.Execute;
begin
while not Terminated do
begin
Sleep(5000);
Synchronize(DoSomething);
end;
end;

sondern

procedure TMyThread.Execute;
begin
while not Terminated do
begin
Synchronize(DoSomething);
end;
end;

procedure DoSomething;
begin
Sleep(5000);
end;

Macht das einen Unterschied, wenn das Sleep ebenfalls mit synchronisiert
wird? Ich hab schon das Gefühl irgendwie.
Da ich nicht direkt Sleep sondern die Wait Methode aufrufe, welche wiederum
den Sleep Aufruf kapselt und zudem ein AppProcMsg ausführt, können andere
Threads während des Wait aufrufes weiterarbeiten. Ansonsten steht das
gesamte Programm still, bis der Timeout abgelaufen ist.

Grüße
Christian
Christian Gudrian
2008-05-06 07:31:18 UTC
Permalink
Post by Christian Bode
Macht das einen Unterschied, wenn das Sleep ebenfalls mit synchronisiert
wird? Ich hab schon das Gefühl irgendwie.
Du bist lustig.

Synchronize macht nichts anderes, als die übergebene Methode im Kontext
des Haupt-Threads auszuführen.

Du legst also (wie ich schon vermutete) den Haupt-Thread und somit die
Nachrichtenschleife lahm.

Christian
Christian Bode
2008-05-06 12:42:08 UTC
Permalink
Post by Christian Gudrian
Du legst also (wie ich schon vermutete) den Haupt-Thread und somit die
Nachrichtenschleife lahm.
Tschuldigung :-)

Ich denke mein Problem ist es, sicherzustellen, das der HauptThread bereits
läuft, bevor der andere Thread anläuft.
Dies ist nicht immer der Fall, was ich mich etwas wundert. Hängt aber sicher
mit der internen Verarbeitung des Synchronize zusammen.

Grüße
Christian
Christian Gudrian
2008-05-06 13:05:20 UTC
Permalink
Post by Christian Bode
Ich denke mein Problem ist es, sicherzustellen, das der HauptThread
bereits läuft, bevor der andere Thread anläuft.
Wer anderes als der Haupt-Thread soll denn den Neben-Thread anwerfen?
Die Tatsache, dass er überhaupt existiert, soll dem Neben-Thread doch
Grund genug für die Annahme sein, dass ebenfalls ein Haupt-Thread
existiert. Abgesehen davon: ein einfaches Warten auf irgendwas
erfordert kein Synchronize.
Post by Christian Bode
Hängt aber sicher mit der internen Verarbeitung des Synchronize
zusammen.
Synchronize macht im Prinzip nichts anderes, als den Zeiger auf die
übergebene Methode in eine Schlange zu stellen, die vom VCL-Thread in
regelmäßigen Abständen abgearbeitet wird. Synchronize wartet dabei so
lange, bis die Methode vom VCL-Thread aufgerufen wurde.

Christian
Christian Bode
2008-05-06 14:58:26 UTC
Permalink
Post by Christian Gudrian
Wer anderes als der Haupt-Thread soll denn den Neben-Thread anwerfen?
Die Tatsache, dass er überhaupt existiert, soll dem Neben-Thread doch
Grund genug für die Annahme sein, dass ebenfalls ein Haupt-Thread
Ja mit Haupt-Thread meine ich in dem Fall nicht den VCL-Thread, sondern den
zusätzlichen Haupt-Thread neben dem Event-Thread in meinem Programm. Obwohl
beide Threads nacheinander erzeugt werden, gibt das noch keine
Gewährleistung, das auch zuerst der Haupt-Thread als erstes executed wird.
Post by Christian Gudrian
existiert. Abgesehen davon: ein einfaches Warten auf irgendwas
erfordert kein Synchronize.
Das nicht, aber das ganze ist auch etwas komplexer. Ich habe es hier nur so
geschrieben um das ganze etwas zu verdeutlichen.

Grüße
Christian
Christian Gudrian
2008-05-06 15:05:06 UTC
Permalink
Post by Christian Bode
Ja mit Haupt-Thread meine ich in dem Fall nicht den VCL-Thread, sondern den
zusätzlichen Haupt-Thread neben dem Event-Thread in meinem Programm. Obwohl
beide Threads nacheinander erzeugt werden, gibt das noch keine
Gewährleistung, das auch zuerst der Haupt-Thread als erstes executed wird.
Warum lässt Du dann nicht den Haupt-Thread den Event-Thread erzeugen,
wenn letzterer so auf den ersten angewiesen ist?
Post by Christian Bode
Das nicht, aber das ganze ist auch etwas komplexer.
Willkommen im Reich der Nebenläufigkeit. :)

Christian
Christian Bode
2008-05-06 15:15:29 UTC
Permalink
Post by Christian Gudrian
Warum lässt Du dann nicht den Haupt-Thread den Event-Thread erzeugen,
wenn letzterer so auf den ersten angewiesen ist?
Das habe ich auch schon überlegt, aber eigentlich hat der Event-Thread mit
dem Haupt-Thread nix direkt zu tun, von daher wollte ich ihn nicht damit in
Verbindung bringen. Aber rum wie num ... ich glaub ich werd das alles noch
mal erneut überdenken müssen, wenn ich hier so eure Erklärungen lese. Hat
zwar bisher größtenteils gut funktioniert, aber ich möchte es wenn schon mit
Threads auch richtig und elegant machen. Im Moment sieht es wohl eher nach
dem Gegenteil aus. :-)

Grüße
Christian
Christian Gudrian
2008-05-07 08:30:10 UTC
Permalink
Post by Christian Bode
Das habe ich auch schon überlegt, aber eigentlich hat der
Event-Thread mit dem Haupt-Thread nix direkt zu tun, von daher wollte
ich ihn nicht damit in Verbindung bringen.
Zu spät, würde ich sagen. Die Verbindung ist schon da.
Post by Christian Bode
Hat zwar bisher größtenteils gut funktioniert
Funktion ist kein Qualitätsmerkmal. Eine Software, die nicht das tut,
was sie soll, ist nicht von minderer Qualität, sondern schlichtweg
unbrauchbar.

Christian
Christian Bode
2008-05-07 14:47:39 UTC
Permalink
Post by Christian Gudrian
Funktion ist kein Qualitätsmerkmal. Eine Software, die nicht das tut,
was sie soll, ist nicht von minderer Qualität, sondern schlichtweg
unbrauchbar.
Ja das war gut in Worte gefasst. In der Praxis wird man jedoch immer wieder
vor solche Fälle gestellt.

Grüße
Christian
Arno Garrels
2008-05-06 13:57:26 UTC
Permalink
Post by Christian Bode
Ich denke mein Problem ist es, sicherzustellen, das der HauptThread
bereits läuft, bevor der andere Thread anläuft.
Dies ist nicht immer der Fall, was ich mich etwas wundert. Hängt aber
sicher mit der internen Verarbeitung des Synchronize zusammen.
Synchronize wartet bis der Hauptthread bereit ist, sprich Messages
verarbeitet, dann wird die ThreadMethode im Kontext des Hauptthreads
aufgerufen und ausgeführt.

--
Arno Garrels
Marian Aldenhövel
2008-05-06 08:18:43 UTC
Permalink
Hi,
Post by Christian Bode
Macht das einen Unterschied
Ja, einen entscheidenden.

Der Sinn von Synchronize besteht darin, Code vom sekundären Thread
anzuschubsen, aber im Kontext des Hauptthreads auszuführen. Also
legst Du mit dem Sleep() in der per Synchronize() aufgerufenen
Methode den Hauptthread schlafen.

Ende vom Lied.

Wenn Dein Thread tatsächlich eigentlich nur wartet, seine eigentliche
Arbeit aber in einer per Synchronize() aufgerufenen Methode verrichtet,
so ist eigentlich nicht viel mehr als ein Timer. Dann nimm' auch einen
und erspare Dir die ganze Sync/Deadlock-Problematik ganz.

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Christian Bode
2008-05-06 12:34:46 UTC
Permalink
Post by Marian Aldenhövel
Ja, einen entscheidenden.
Ich habs schon befürchtet. Ich glaub jetzt ist mir das erstmal im Großen und
Ganzen klar was da passiert.
Post by Marian Aldenhövel
Wenn Dein Thread tatsächlich eigentlich nur wartet, seine eigentliche
Arbeit aber in einer per Synchronize() aufgerufenen Methode verrichtet, so
ist eigentlich nicht viel mehr als ein Timer.
Darüber habe ich schon nachgedacht, aber im EventThread passiert ja ein
wenig mehr, wie nur das Sleep. Das warten auf eine Event per Timer oder
WaitForSingle Object ist für mich aber nicht die Lösung, da ich innerhalb
des Wartezeitraums Application.ProcessMessages aufrufen muss, sodass andere
Threads abgearbeitet werden können und das Programm bedienbar bleibt.

Ich möchte noch mal kurz beschrieben was ich eigentlich mache :-)

Der Thread (MainThread) kümmert sich um die grafische Darstellung von
Animationen.
Der Thread (EventThread) verarbeitet eine Event-Queue. Events können durch
Animationen, Maus und Tastatur u.a. ausgelöst werden und werden sequenziell
abgarbeitet. Innerhalb eines Events kann auch eine Pause gemacht werden.
Dies geschieht u.a. durch den Aufruf von Sleep. Das allerdings bereitet mir
die besagten Probleme.

Da sowohl MainThread als auch der EventThread mit der VCL zu tun haben, muss
ich mit Synchronize arbeiten.

Grüße
Christian
Marian Aldenhövel
2008-05-06 13:54:37 UTC
Permalink
Hallo,
Das warten auf eine Event per Timer oder WaitForSingle Object ist für
mich aber nicht die Lösung, da ich innerhalb des Wartezeitraums
Application.ProcessMessages aufrufen muss, sodass andere
Threads abgearbeitet werden können und das Programm bedienbar bleibt.
Der Satz demonstriert ein fundamentales Verständnisproblem: Damit Threads
nebenläufig sind braucht es kein ProcessMessages. Auch nicht, damit der
Hauptthread am Leben bleibt.

Den Eindruck gewinnst Du nur durch Dein Synchronize, das die Nebenläufigkeit
wieder blockiert.

Alternativ lässt Du Dein Programm mit einem einzigen Thread ablaufen. Dann
brauchst Du für längere Operationen das Process- oder HandleMessage, hast
keine Thread-Synchronisationsprobleme, musst aber halt GUI-Intern etwas
aufpassen. Reentrante Buttonclick-Handler etc...

Auf beiden Wegen lassen sich Anwendungen bauen, die das UI nicht blockieren.
Vermischen führt aber meist zu zusätzlichen Schwierigkeiten.

Ein Kriterium ist die Frequenz und Intensität mit der die nebenläufigen
Tätigkeiten auf die Oberfläche Einfluß haben sollen:

Niedrig -> Threads, alles schön entkoppelt, Korrektheit eher leichter
sicherzustellen.

Hoch -> Keine Threads, die synchronisieren sich ja sowieso nur tot.

Der wesentlichste Unterschied beider Methoden ist der, daß nur multithreaded
Anwendungen von Mehrkernprozessoren, real oder virtuell, profitieren können.
Aber auch das wird meist überbewertet, die Anwendung läuft ja nicht alleine
auf dem System. Dann verwenden eben andere Teile des Systems die anderen
CPUs :-).
Da sowohl MainThread als auch der EventThread mit der VCL zu tun haben, muss
ich mit Synchronize arbeiten.
Nicht notwendigerweise.

Wenn Deine Threads häufig Synchronize brauchen, dann sind sie als Threads
falsch angelegt. Siehe oben.

Lass' Deine Threads zum Beispiel nicht selber malen sondern lediglich die
Daten bereitstellen, die gemalt werden sollen. Du sorgst dafür, daß das
Lesen dieser Daten durch den Hauptthread sicher gegenüber konkurrierenden
Updates durch die Daten produzierenden Threads ist. Das geht auch mit
anderen Mechanismen als dem brutal-synchronize und ist je nach Art besagter
Daten unter Umständen auch gar nicht nötig.

http://www.eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/ToC.html

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Christian Bode
2008-05-06 15:19:34 UTC
Permalink
Post by Marian Aldenhövel
Lass' Deine Threads zum Beispiel nicht selber malen sondern lediglich die
Daten bereitstellen, die gemalt werden sollen. Du sorgst dafür, daß das
Lesen dieser Daten durch den Hauptthread sicher gegenüber konkurrierenden
Updates durch die Daten produzierenden Threads ist. Das geht auch mit
anderen Mechanismen als dem brutal-synchronize und ist je nach Art besagter
Daten unter Umständen auch gar nicht nötig.

Danke erstmal für deine Hilfe und Erläuterungen ... ich muss wohl noch mal
die grundlegende Herangehensweise der Verarbeitung überdenken, bevor ich
weiter mache. Sonst stehe ich vielleicht zukünftig vor dem nächsten Problem.
Ist aber auch keine einfache Materie nur dachte ich, ich hätte langsam den
Dreh raus. Lange Rede kurzer Sinn ... ich muss mir was anderes überlegen.

Grüße
Christian
Christian Bode
2008-05-07 15:30:47 UTC
Permalink
Post by Marian Aldenhövel
Wenn Deine Threads häufig Synchronize brauchen, dann sind sie als Threads
falsch angelegt. Siehe oben.

Zumindest den Thread (was auch ebenso gut ein Timer sein könnte), welcher
prüft es neue Dinge zu zeichnen gibt, würde ich gern als solchen belassen.
Ich könnte den gesamten Ablauf auch in den VCL-Thread packen, jedoch macht
sich eine Endlos-Schleife dort nicht so gut und ich muss wieder mit
Application.ProcessMessages arbeiten.
Bisher ist der Thread so aufgebaut, das er ein Event erzeugt, wenn er neue
Dinge es neue Dinge zum Zeichnen gibt. In diesem Event findet dann die
Neuzeichnung statt. Das hat bisher auch prima funktioniert, da ich den
Thread ja mit der VCL synchronisiert hatte.
Wenn ich jetzt auf die Synchronisierung verzichten möchte, hab ich ja
trotzdem noch das Problem, das der Thread asynchron läuft.
Wenn ich jetzt stattdessen in dem ThreadEvent ein weiteres Event des
VCL-Threads auslöse, habe ich damit sichergestellt, dass dieses Ereignis
innerhalb des VCL-Threads sauber abgarbeitet wird und der Thread in der
Schleife erst weiterläuft und das Ereignis erneut auslöst, wenn es zuvor
sauber abgearbeitet wurde.
Bsp:

TMyThread = class(TThread)
private
FEvent: TNotifyEvent;
protected
procedure Execute; override;
public
constructor Create;
property OnExecute: TNotifyEvent read FEvent write FEvent;
end;

procedure TMyThread.execute;
begin
while not (Terminated) do
begin
if Assigned(FEvent) then FEvent(Self);
Sleep(10);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
aThread := TMyThread.Create;
aThread.OnExecute := OnVCLEvent;
end;

procedure TForm1.OnVCLEvent(Sender: TObject);
begin
ListBox1.Items.Add('Event');
Sleep(5000);
end;

Eigentlich finde ich auch diese Lösung etwas spartanisch. *g*
Marian Aldenhövel
2008-05-07 15:54:59 UTC
Permalink
Hallo,
Post by Christian Bode
procedure TMyThread.execute;
begin
while not (Terminated) do
begin
if Assigned(FEvent) then FEvent(Self);
Sleep(10);
end;
end;
Dieser Code wird vom Thread ausgeführt. Und ein VCL-Ereignis
ist nichts anderes als ein Methodenzeiger. Also wird auch der
Handler im Kontext des sekundären Threads ausgeführt (in
Abwesenheit von Synchronize()).
Post by Christian Bode
procedure TForm1.OnVCLEvent(Sender: TObject);
begin
ListBox1.Items.Add('Event');
Sleep(5000);
end;
Und der greift auf visuelle Komponenten zu. Darf sekundärer Thread
nicht. Bumm. Programm tot.

Verwendest Du hingegen Synchronize(VCLEvent), so ist Dein TMyThread
wirklich nur ein Ding, das periodisch eine Nachricht in die
Warteschlange des VCL-Hauptprogramms stellt. exakt genauso
funktioniert TTimer.

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Christian Bode
2008-05-07 16:32:23 UTC
Permalink
Post by Marian Aldenhövel
Verwendest Du hingegen Synchronize(VCLEvent), so ist Dein TMyThread
wirklich nur ein Ding, das periodisch eine Nachricht in die
Warteschlange des VCL-Hauptprogramms stellt. exakt genauso
funktioniert TTimer.

Nun gerade auf Synchronize wollte ich eigentlich verzichten. Gibt es noch
andere Möglichkeiten, für einen Abgleich mit dem VCL-Thread um eben zu
verhindern das die gesamte Zeichnung innerhalb des Threads und das
Synchronize gekapselt ist?
Mal absgesehen von der Möglichkeit es gleich in den VCL-Thread zu verlagern.

Grüße
Christian
Marian Aldenhövel
2008-05-07 19:51:11 UTC
Permalink
Hallo,
Post by Christian Bode
andere Möglichkeiten, für einen Abgleich mit dem VCL-Thread um eben zu
verhindern das die gesamte Zeichnung innerhalb des Threads und das
Synchronize gekapselt ist?
Du kannst im Thread malen. Aber dann nicht auf die VCL-Controls. Du kannst
natürlich, wenn Du sicherstellst, daß sich die Threads nicht dabei auf
die Füße treten, durchaus einen Thread zum Beispiel eine Bitmap erzeugen
und dann vom Hauptthread auf den Bildschirm klatschen lassen. Das geht
natürlich auch kachelweise.

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
Arno Garrels
2008-05-07 16:29:10 UTC
Permalink
Post by Christian Bode
Eigentlich finde ich auch diese Lösung etwas spartanisch. *g*
Und wo wird da was synchronisiert? Das sollte wenigstens ab und zu
machtig krachen.

Schau dir mal das angehängte Beispiel an. Es zeigt, wie man mit
Messages Daten zwischen dem Haupt und einem Arbeitsthread
austauschen kann. "Daten" ist hier lediglich ein Integer, aber das
kann man beliebig ändern, in dem man einen Pointer auf Daten mit der
Message verschickt. Ich hoffe auf die Schnelle keinen Fehler
eingebaut zu haben, andernfalls wird hier sicherlich schnell jemand
fündig.

Erzeuge eine neue Delphi Win32 VCL Anwendung und kleb ein Memo und
einen Button auf die Form, dann ersetze den erzeugten Quellcode mit
dem angehängten. Nicht vergessen die Ereignisse im Objektinspektor
zuzuweisen!


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

const
WM_TUWAS = WM_USER + 1;
WM_JOB_FINISHED = WM_USER + 2;
WM_THREAD_FINISHED = WM_USER + 3;

type
TTuWas = class(TThread)
protected
procedure Execute; override;
public
Ready : Boolean;
end;

TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
FThread : TTuWas;
FJobCnt : Integer;
procedure WmJobFinished(var Msg: TMessage); message WM_JOB_FINISHED;
procedure WmThreadFinished(var Msg: TMessage); message WM_THREAD_FINISHED;
procedure TuWas;
public
{ Public-Deklarationen }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

{ TTuWas }

procedure TTuWas.Execute;
var
Msg : TMsg;
begin
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE); // Initialisiert die Botschaftenschlange
Ready := TRUE; // Zeigt an, dass der Thread bereit ist Messages zu empfangen
try
while GetMessage(Msg, 0, 0, 0) do // Warten auf Messages, gibt False zurück wenn WM_QUIT empfangen wird
begin
if Terminated then Exit;
if (Msg.message = WM_TUWAS) then
begin
Sleep(Msg.wParam); // Hier ist die Aufgabe das Schlafen, Parameter wird mit Message gesendet
PostMessage(Form1.Handle, WM_JOB_FINISHED, 0, Msg.LParam); // Den Hauptthread benachrichtigen, dass dieser Job erledigt wurde
end
else begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
finally
PostMessage(Form1.Handle, WM_THREAD_FINISHED, Integer(Self), 0);
end;
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Clear;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if FThread <> nil then
begin
FThread.Terminate;
PostThreadMessage(FThread.ThreadID, WM_QUIT, 0, 0);
while FThread <> nil do
begin
Application.ProcessMessages;
Sleep(10);
end;
end;
end;

procedure TForm1.TuWas;
begin
if FThread = nil then
begin
FThread := TTuWas.Create(TRUE);
FThread.FreeOnTerminate := FALSE;
FThread.Resume;
while not FThread.Ready do
Sleep(10);
end;
Inc(FJobCnt);
PostThreadMessage(FThread.ThreadID, WM_TUWAS, 1000, FJobCnt);
Memo1.Lines.Add('Job #' + IntToStr(FJobCnt) + ' in Thread-Botschaftenschlange gestellt');
end;

procedure TForm1.WmJobFinished(var Msg: TMessage);
begin
Memo1.Lines.Add('Job #' + IntToStr(Msg.LParam) + ' beendet');
end;

procedure TForm1.WmThreadFinished(var Msg: TMessage);
begin
FThread.WaitFor;
Memo1.Lines.Add('Thread beendet');
FreeAndNil(FThread);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
TuWas;
end;

end.
Arno Garrels
2008-05-06 13:57:33 UTC
Permalink
Post by Christian Bode
Ich möchte noch mal kurz beschrieben was ich eigentlich mache :-)
Der Thread (MainThread) kümmert sich um die grafische Darstellung von
Animationen.
Der Thread (EventThread) verarbeitet eine Event-Queue. Events können
durch Animationen, Maus und Tastatur u.a. ausgelöst werden und werden
sequenziell abgarbeitet. Innerhalb eines Events kann auch eine Pause
gemacht werden. Dies geschieht u.a. durch den Aufruf von Sleep. Das
allerdings bereitet mir die besagten Probleme.
Dein Design bleibt irgendwie rätselhaft. Du könntest vermutlich
folgendermaßen vorgehen:
Der Arbeitsthread startet und wartet auf Arbeit ;-)
Hauptthread weist ihn an etwas zu tun, in dem ein Eventhandel
signalisiert oder ihm eine Message geschickt wird.
Der Thread beginnt nun mit der Ausführung der Aufgabe. In dieser Zeit
sollte der Hauptthread nicht auf das Ergebnis warten, andernfalls
könnte die Arbeit ja gleich im Hauptthread erledigt werden. Wenn der
Arbeitsthread den Job ausgeführt hat, muss er nur noch das Ergebnis
dem Hauptthread mitteilen. Das kann er per Synchronize tun, danach
wartet er auf den nächsten Job, oder holt sich einen aus der
Warteschlange ab, oder wie auch immer. Der Hauptthread aktualisiert
die Anzeige entsprechend dem, vom Thread gelieferten Job-Ergebnis.
Post by Christian Bode
Da sowohl MainThread als auch der EventThread mit der VCL zu tun
haben, muss ich mit Synchronize arbeiten.
Synchronize ist die sicherste Holzhammermethode für die
Synchronization aber eben auch anfällig für Deadlocks.
Wenn du in der Threadmethode wartest, kannst du lange warten, denn
dann versetzt du sowohl den Hauptthread als auch der Arbeitsthread in
Tiefschlaf.

--
Arno Garrels
Lesen Sie weiter auf narkive:
Loading...