![]() |
Synchronisieren von hauptthread und Teilthread
Hallo da draußen !
In meinem Programm scheinen RTEs daher zu kommen, dass ein bestimmter Thread, nennen wir ihn Thread_A, bestimmte Daten verändert und der Hauptthread, z.B. in einem TimerEvent oder einem form-Refresh manchmal genau in dem Moment auf diese Daten zugreift, wenn es offensichtlich nicht passt. Ich habe versucht, den Code in der Timerroutine bzw. beim Aufbau der Anzeige des Hauptthreads mit CriticalSection.enter / Leave solange auszusperren, bis der thread_a mit der Berechnung fertig ist. Aber dies klappt offensichtlich nicht. Der Code im Hauptthread hinter dem "cs.enter" wurde immer ausgeführt. Darin besteht mein Problem. Weitere Erkentnisse in diesem Zusammenhang: o Im Hauptthread kann man mehrmals dasselbe criticalSection.enter aufrufen. o Der thread_a bleibt beim ersten cs.enter stehen und läuft beim nächsten cs.Leave aus dem Hauptthread wieder weiter. o N x cs.enter erfordern auch N x cs.leave, damit thread_a wieder läuft o Es gibt einen Totalstillstand, wenn zuerst cs.leave ohne vorheriges cs.enter aufgerufen wird Vielen Dank vorab für einen Hinweis in dieser Sache. |
Re: Synchronisieren von hauptthread und Teilthread
Abend!
|
Re: Synchronisieren von hauptthread und Teilthread
Danke für die extrem schnelle Antwort (5 min !, und dass um 24:00)
Ich habe die Anregungen ausgewertet und z.T. sofort getestet, aber sie lösen mein Problem wahrscheinlich nicht: Zitat:
Zitat:
Vielleicht ist es relevant, wie das beschreiben abläuft: Es wird im thread_a in der CS eine Sensorik angesprochen und deren Meßwerte u.a. in eine ASC-Datei geschrieben Der haupthtread soll dann z.B. in der Timerroutine oder auf BtnClick diese ASC-Datei innerhalb einer CS wieder parsen und die Daten grafisch anzeigen. Da stört es natürlich, wenn thread_a das File gerade schreibt und der Hauptthread schon wieder darauf zugreifen will. Dies hat komischerweise auch eine Weile geklappt. Neuerdings treten aber totale blockaden auf, die ich von früher nicht kannte. Zitat:
Ich würde heute die ganze Sache anders anpacken, aber es handelt sich um alten Code, der noch ein bischen halten soll. Wenn cs.enter / cs.leave klappen würde, wäre ja alles gut. Zitat:
Habe ich getestet, verhält sich aber in Bezug auf Hautpthread / Thread wie cs.enter/cs.leave, geht also nicht: Ich kann im Hauptthread mehrmals MultiReadExclusiveWriteSynchronizer.BeginWrite aufrufen, wobei der Code dahinter immer abgearbeitet wird. Ich versuche jetzt mal WaitForMultipleObjects als Warte - Mechanismus. Für Hinweise bin ich weiterhin dankbar Tom. |
Re: Synchronisieren von hauptthread und Teilthread
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:
o Dann kommen die Threads oder der hauptthread mittes WaitForSingleObject( Event_0, 10 ) irgenwann zufällig zum Zuge
event0 := CreateEvent(nil, False, False, nil);
SetEvent(Event0); o nach der Datenbearbeitung oder - Nutzung im Thread/HauptThread wird direkt wieder ein Event aktiviert
Delphi-Quellcode:
bzw.
//----------------------------------------------
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;
Delphi-Quellcode:
o Damit kommem alle im richtigen Moment mal dran, insbesondere eben auch der Hauptthread
//----------------------------------
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; 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
Delphi-Quellcode:
o Der Thread führt diese Routine per Synchronize() aus.
auftObj.thread_messen.onMeasThreadProc := pr_ThreadProc;
Delphi-Quellcode:
//----------------------------------------------------------------
procedure Tthread_messen.pr_SyncProc; //---------------------------------------------------------------- begin onMeasThreadProc(self) end;
Delphi-Quellcode:
o Während der Ausführung kann kein Repaint oder Neuaufbau der Anzeigen erfolgen, da die Ereignisprozedur kein KeepWindowsAlive oder Sleep() enthält.
if assigned(onMeasThreadProc) then
begin Synchronize(pr_SyncProc); end; 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 ! |
Re: Synchronisieren von hauptthread und Teilthread
Nur ganz am Rande:
Zitat:
|
Re: Synchronisieren von hauptthread und Teilthread
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Noch kurz was zum Code:
Delphi-Quellcode:
Das geht doch insgesamt auch mit (vorrausgesetzt die Reihenfolge mit dem sleep ist unwichtig)
while TRUE do
begin sleep(1); if Terminated then begin //warum hier ein begin-end-Block? exit end
Delphi-Quellcode:
while not terminated do
begin sleep(1); |
Re: Synchronisieren von hauptthread und Teilthread
Vielleicht noch eine Idee. Anstelle eines TTimers könntest du auch den Thread als Timer benutzen, also den Aufruf des Synchronisations-Ereignises von der vergangenen Zeit abhängig machen. (falls das die Sache irgendwie vereinfacht)
|
Re: Synchronisieren von hauptthread und Teilthread
Danke für die bisherigen Hinweise !
Mein Hauptproblem bestand offensichtlich in der Annahme, dass auch im Hauptthread mehrere scheinbar parallelen Prozesse wie o Timer-Routinen in verschiedenen gleichzeitig sichtbaren Fenstern o dazwischen auch mal ein BtnClick usw., jeweils mit gewisser Bearbeitungszeit mit CS.enter/cs.Leave voreinander geschützt werden können. Das geht - nun ist mir das klar - nicht, weil immer derselbe Thread = Hauptthread sperren soll, dabei aber nur irgendeinen Zähler im CS-Objekt verändert. Alles andere wäre ja auch komplette unsinnig, da sofort ein Deadlock entstehen würde. Das im Moment noch Rätselhafte für mich ist allerdings, dass durch den Aufruf von cs.enter im Haupthtread bei gleichzeitig laufenden Nebenthreads mit CriticalSections das Programm komplett blockiert wird, also nicht nur die mit cs.enter geschützten Nebenthreads blockieren. Zu einem cs.leave komme ich dann gar nicht mehr. Noch merkwürdiger: das scheint erst seit kurzem aufzutreten. Ich werde aber nochmals testen, ob das im Zusammenhang mit dem Aufruf von Mainthread-Methoden z.B. einer Form auftritt. Es könnte sein, dass dann gerade die Zeile
Delphi-Quellcode:
diesen Effekt bewirkt.
Synchronize(form1.DoEtwas)
Ich habe mittlerweile den Rat u.a. von Dani beherzigt: Kritischen Abschnitte des Hauptthreads werden in einen Event gelegt, der vom Nebenthread ausgelöst wurde. Innerhalb des Events wird dann die Aufgabe ohne Aufruf von KeepWindowsAlive erledigt, so dass parallel dazu weder ein anderer Thread noch der Hauptthread reinspucken kann. Das scheint zu klappen. Danke noch mal an alle ! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:10 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz