Nach einigen Stunden Beschäftigung mit dem Thema sieht die Situation so aus:
1. Das Sperren des Hauptthreads klappt mit WaitForSingleObject(Event_0,INFINITE). Und zwar so gut, dass er bei falscher Anwendung niemals wieder ans Laufen kommt.
Falsche Anwendung bedeutet:
a) Entweder wurde nicht organisiert, dass mindestens ein Event, z.B. von einem Thread, kommt.
b) Oder mitten in die TimerRoutine platzt dann ein BtnClickEvent, der ebenfalls WaitForSingleObject(Event_0,INFINITE) enthält und damit seinerseits auf einen Event wartet, der später eigentlich von der Timerroutine ausgelöst werden sollte, um die Lock/Unlock-Kette am laufen zu halten.
Klassischer Deadlock !
Die (für mich) richtige Anwendung wäre:
o Es wird ein Vorratsevent direkt nach dem Erzeugen des Events aktiviert
Delphi-Quellcode:
event0 := CreateEvent(nil, False, False, nil);
SetEvent(Event0);
o Dann kommen die Threads oder der hauptthread mittes WaitForSingleObject( Event_0, 10 ) irgenwann zufällig zum Zuge
o nach der Datenbearbeitung oder - Nutzung im Thread/HauptThread wird direkt wieder ein Event aktiviert
Delphi-Quellcode:
//----------------------------------------------
procedure TForm1.Timer1Timer(Sender: TObject);
//----------------------------------------------
var wfmo: DWORD;
s:
string;
tp2: int64;
ccc: integer;
begin
Timer1.Enabled:=FALSE;
if fn_GetTimerSec(tp1) > 4.0
then
begin
if (WaitForSingleObject(Event0,10) = WAIT_OBJECT_0)
then
begin
pr_StartTimer(tp2);
ccc:=0;
repeat
s1:= '
mainThread 2 Sekunden Arbeit '+__s(ccc);
label1.Caption := s1;
label1.refresh;
inc(ccc);
until fn_GetTimerSec(tp2) > 2.0;
pr_StartTimer(tp1);
s1:= '
mainThread idle';
SetEvent(Event0);
end;
end;
inc(
cc);
label1.Caption := s1 + IntToStr(
cc)+'
';
label1.refresh;
Timer1.Enabled:=TRUE;
end;
bzw.
Delphi-Quellcode:
//----------------------------------
procedure TThread_a.Execute;
//----------------------------------
var tp2,tp1: Int64;
ccc: Integer;
begin
inherited;
pr_StartTimer(tp1);
s1:= '
Thread_a idle ';
cc:=0;
pr_StartTimer(tp1);
while TRUE
do
begin
sleep(1);
if Terminated
then
begin
exit
end
if fn_GetTimerSec(tp1) > 4.0
then
begin
if WaitForSingleObject(Event0,10) = WAIT_OBJECT_0
then
begin
ccc:=0;
pr_StartTimer(tp2);
repeat
s1:= '
Thread_a Event0, 2 Sekunden Arbeit ! '+__s(ccc);
sleep(1);
inc(ccc);
until fn_GetTimerSec(tp2) > 2;
s1:= '
Thread_a idle ';
show;
pr_StartTimer(tp1);
SetEvent(Event0);
end;
end;
inc(
cc);
end
end;
o Damit kommem alle im richtigen Moment mal dran, insbesondere eben auch der Hauptthread
Meine Abhilfe sieht jetzt so aus:
o Die Teilthreads, aber nicht der hauptthread, werden weiterhin mit CriticalSection voreinander geschützt
o Der HauptThread stellt seine Auswerte/Anzeigeroutine dem Thread per EreignisProzedur zur Verfügung
auftObj.thread_messen.onMeasThreadProc := pr_ThreadProc;
o Der Thread führt diese Routine per Synchronize() aus.
Delphi-Quellcode:
//----------------------------------------------------------------
procedure Tthread_messen.pr_SyncProc;
//----------------------------------------------------------------
begin
onMeasThreadProc(self)
end;
Delphi-Quellcode:
if assigned(onMeasThreadProc) then
begin
Synchronize(pr_SyncProc);
end;
o Während der Ausführung kann kein Repaint oder Neuaufbau der Anzeigen erfolgen, da die Ereignisprozedur kein KeepWindowsAlive oder Sleep() enthält.
o Erst wenn die Auswerte/Anzeigeroutine komplett am Stück beendet ist, kommt der HauptThread zum Zuge. Dann ist alles Kritische aber schon erledigt.
Für weitere erhellende Kommentare bin ich selbstverständlich dankbar. Ansonsten scheint mein spezielles Problem durch diese Verfahrensweise gelöst zu sein.
Danke nochmals an Dani für die schnelle Reaktion.
Klasse Forum, klasse Seite !