![]() |
Deadlock beim stoppen eines Arbeiter Threads
Hallo und guten Abend,
ich habe folgendes Programmkonstrukt: 1. Hauptthread (der die VCL bedient) 2. Arbeiterthread, der bestimmte Werte über eine Schnittstelle holt und diese über Synchronize an den Hauptthread übergibt. Ich habe nun folgendes Problem, der 2. Thread soll ja weitesgehend selbsständig im Hintergrund laufen, und sich immer dann melden, wenn er neue Werte hat... Das klappt auch, mein Problem ist, dass ich ihn synchronisieren muss mit dem Hauptthread, und zwar genau dann, wenn ich vom Hauptthread auch auf die Schnittstelle zugreifen will..
Delphi-Quellcode:
Kommt nicht Frage, da ich nie genau weiß in welchem Zustand mein Schnittstellenobjekt gerade ist(könnte ja sein, dass er gerade gesendet hat)..
aThread.Suspend;
Ich muss also den Thread in einem definierten Zustand stoppen können, habe das zurzeit folgendermaßen gelöst:
Delphi-Quellcode:
procedure TCyclicData.Execute;
var I: Integer; merkeTimeOutProz: procedure (Sender:TObject) of object; begin while Not(Terminated) do begin // Synchronize(fSTK.Kommunikation.Sch); merkeTimeOutProz:=fSTK.Kommunikation.OnTimeOut; fSTK.Kommunikation.OnTimeOut:=Self.ShowTimeOut; // if MaskeAnalogAusgangAktiv then exit; feinDataset.aktuellerFlow:=fSTK.aktFlow; feinDataset.Summation:=fSTK.Summation; feinDataset.Status:=fSTK.Status; feinDataset.Counts:=fSTK.Kommunikation.Counts; // feinDataset.Dezimalstelle:=fSTK.DezimalStelle; // =====KALIBRIERUNG======// if (MaskeKalibrierungAktiv) then begin if (fSTK.isMulti) then begin for I := 1 to length(fChannels) do begin // Laufe jeden verfügbaren Kanal durch if fChannels[i] then begin fSTK.KanalWechsel:=I; fDataSetKalib[I].KanalNr:=I; fDataSetKalib[I].KanalAktiv:=true; fDataSetKalib[I].DeltaTempCorr:=fSTK.DeltaTempCorr; fDataSetKalib[I].T2:=fSTK.T2; fDataSetKalib[I].DeltaTRoh:=fSTK.DeltaTRoh; end; // of if fChannels[i] then end; // of for I := 0 to length(fChannels) - 1 do end; // of if fSTK.isMulti then Synchronize(SyncKanalData); feinDataset.T2:=fSTK.T2; feinDataset.DeltaTRoh:=fSTK.DeltaTRoh; end; // of if MaskeKalibrierungAktiv then // =====SONSTIGES======// if (MaskeSonstigesAktiv) then begin feinDataset.aktuellerFlow:=fSTK.aktFlow; feinDataset.aktFlowSkaliert:=fSTK.aktFlowSkaliert; feinDataset.DeltaTRoh:=fSTK.DeltaTRoh; end; // of if MaskeSonstigesAktiv then if (MaskeDataloggingAktiv) then begin // Lese Anzahl DS feinDataset.DataSetCounts:=fSTK.DataSetCounts; fSTK.DataLogConfig; end; // of if (MaskeDataloggingAktiv) then //-----------------------------------> WICHTIGER BEREICH if (fShouldStopp) then begin fSyncObj.Acquire; fIsStopped:=true; fSyncObj.Release; while fShouldStopp do begin Sleep(10); if Self.Terminated then exit; end; //-----------------------------------> WICHTIGER BEREICH fSyncObj.Acquire; isStopped:=false; fSyncObj.Release; end else begin fSyncObj.Acquire; isStopped:=false; fSyncObj.Release; end; // of if (fShouldStopp) then // if MaskeAnalogAusgangAktiv then exit; fSTK.Kommunikation.OnTimeOut:=merkeTimeOutProz; Synchronize(Self.SyncOnNewData); Sleep(100); end; end; Im Hauptthread setzte ich also die Variable fshouldstopp und warte dann bis der Thread dann die Variable isStopped gesetzt hat, um zuwissen, dass er gestoppt hat. Das passiert folgendermaßen (sollte besser mit Signalen arbeiten, oder??)
Delphi-Quellcode:
Könnt ihr mir helfen, wie ich das besser lösen kann??
(*******************************************************************************
Prozedur zum stoppen des Hintergrund Tasks *******************************************************************************) procedure TFrmMain.StoppBackgroundTask; begin if Assigned(aCyclicDataThread) then begin aCyclicDataThread.PleaseStopp; while (Not aCyclicDataThread.isStopped) do begin if Not(einSTK.Kommunikation.Schnittstelle.isConnected) then exit; if(Application.Terminated) then exit; end; // of while (Not aCyclicDataThread.isStopped) do end; // of if Assigned(aCyclicDataThread) then end;// of procedure TFrmMain.StoppBackgroundTask; //////////////////////////////////////////////////////////////////////////////// Danke euch :cyclops: |
Re: Deadlock beim stoppen eines Arbeiter Threads
[edit]
hab leider editiert, statt zitiert ... also Beitrag is futsch und leider auch nicht mehr in meiner Cache auffindbar :wall: |
Re: Deadlock beim stoppen eines Arbeiter Threads
Hey danke für deinen Post
Werde die Funktion mal testen (am Montag), so wie ich es bisher gelöst habe, kommt es (manchmal) vorher, dass der Hauptthread die fShouldstopp Variable setzte, jedoch die isStopped Variable vom Thread nie gesetzt wird, und der Hauptthread so ewig wartet und die Anwendung dann nicht mehr bedienbar ist. Das Problem tritt auf, wenn ich meine Synchronisationsfunktion drin habe, nehme ich sie raus gibt es gar keine Probleme :?: :?: |
Re: Deadlock beim stoppen eines Arbeiter Threads
Liste der Anhänge anzeigen (Anzahl: 1)
Ich verstehe nicht, wieso Du dir so einen abbrichst:
Ein Workerthread wartet, bis er etwas zu tun bekommt. Wenn Du in schlafen legst, kann er ja nicht mehr warten, da er schläft. :mrgreen: Verwende eine Semaphore oder ein Event, um dem Thread mitzuteilen, das arbeit auf ihn wartet. Da eine Semaphore in etwa ein threadsicherer Zähler mit Benachrichtigungsmechanismus ist, kannst Du sie auch gleich als Zähler für die *Anzahl* der abzuarbeitenden Jobs verwenden. Schau Dir einfach mal den beiliegenden Code an. Er implementiert eine Job-Klasse sowie einen WorkerThread, der Jobs im Hintergrund ausführt. |
Re: Deadlock beim stoppen eines Arbeiter Threads
OK, mit den Booleans kannst du ja nur einmal was übermitteln ... was beim Stoppen (mehrmals bitteWarte=True) nicht stört, aber da dann das erste Zurücksetzten (bitteWarte=False) auch gleich gültig ist würde ja de Thread schon mit arbeiten anfangen, sobald der Erste sagt "also von mir aus kannst du losmachen", obwohl andere noch meinen "eh warte".
demnach mußt du also mitzählen wie oft gelockt ist und erst wenn keiner mehr sperrt dann kann's weitergehn
Delphi-Quellcode:
im Thread dann z.B. 2 Variablen: fShouldStopp und isStopped
Procedure LockedSet(Var X: Boolean; i: Boolean);
ASM LOCK MOV BYTE PTR [&X], &i End; Procedure LockedInc(Var X: Byte); ASM LOCK INC BYTE PTR [&X] End; Procedure LockedDec(Var X: Byte); ASM LOCK DEC BYTE PTR [&X] End; (allerdings direkt freigegeben und nicht über Properties, da direkt darauf zugegriffen wird)
Delphi-Quellcode:
procedure TFrmMain.StoppBackgroundTask;
begin if Assigned(aCyclicDataThread) then begin LockedInc(aCyclicDataThread.ShouldStopp); while not aCyclicDataThread.isStopped do begin Sleep(10); if Terminated then break; end; end; end; procedure TFrmMain.StartBackgroundTask; begin if Assigned(aCyclicDataThread) then LockedDec(aCyclicDataThread.ShouldStopp); end;
Delphi-Quellcode:
if ShouldStopp > 0 then
begin LockedSet(aCyclicDataThread.isStopped, true); while ShouldStopp > 0 do begin Sleep(10); if Terminated then break; end; LockedSet(aCyclicDataThread.isStopped, false); end; Aber dabei kommt ein weiteres Problem auf: denn wenn mehrere mal de Thread gesperrt wird, damit er nicht arbeitet, wenn wo anderes was gemacht wird, dann passiert noch nichts. Allerdings würde doch dann auch an mehreren Stellen (außerhalb des Threads) zugleich gearbeitet (senden/empfangen) werden :warn: Demanch wäre es besser, wenn es nur eine Stelle gibt, welche arbeiten darf. Das würde sich ann mit eine "Arbeitsliste" lösen lassen, wo die anderen Stellen dann nur eintragen mach das und das für mich. Und sag mir dann eventuell noch Bescheid, wenn du fertig damit bist, damit ich weitermachen kann. Der "Arbeitstread" würde dann einfach nur diese Liste abarbeiten und da er der Einzige ist, welcher auf die Shnittstelle ugreift, kann es da keine Probleme geben. :zwinker: |
Re: Deadlock beim stoppen eines Arbeiter Threads
Hi,
danke für eure Antworten, könnt ihr mir denn sagen, wo genau mein Fehler liegt? Das mit der Jobliste habe ich schon in einem anderen Projekt umgesetzt und klappt auch gut, nur hier (in dem alten Projekt) wäre die Umstellung jetzt etwas schwieriger,... Also habe ich diesen Start Stopp Mechanismus,... Meine Frage: wie kann es denn passieren, das obwohl bei mir shouldStopp auf true ist, isStopped dann ( in 1 von 100 Fällen) false ist, und meine Hauptanwendung dann darauf wartet und sich dann aufhängt?? Danke euch |
Re: Deadlock beim stoppen eines Arbeiter Threads
Ohne das jetzt genau zu analysieren, fehlt -denke ich mal- einfach die richtige Synchronisation. TCriticalSections sind wirklich sehr leichtgewichtig und sperren nur unterschiedliche Threads gegenseitig aus. Ein und der selbe Thread kann so oft er will, ein 'Acquire' aufrufen.
Dessenungeachtet würde ich trotzdem den an sich falschen Code (weil keine 'offiziellen' Synchronisationsmechanismen verwendet werden) durch korrekten Code ersetzen. Sprich: Neuschreiben. Alles andere ist doch Quatsch. Das ist Frickelcode und wird einfach nicht korrekt funktionieren. |
Re: Deadlock beim stoppen eines Arbeiter Threads
@alzaimar:
Hi, du schreibst: Zitat:
dass immer ein atomarer Zugriff auf meine Variablen erfolgt? Danke dir! |
Re: Deadlock beim stoppen eines Arbeiter Threads
Mit 'offiziell' meine ich die Verwendung von Events und/oder Semaphoren sowie die zugehörigen Synchronisationsaufrufe 'ReleaseSemaphore','Set/ResetEvent', 'WaitForSingleObject' etc.
Das Einzige, was Du vlt. ändern müsstest, ist diese Logik 'Bitte mal kurz stoppen', denn so ein Thread soll ja eigentlich warten (und dabei keine/kaum CPU-Zeit verbraten) bis etwas zu tun ist. Hier wäre es eben notwendig, diese Metapher in 'Hier haste was zu tun' zu ändern. Wobei ich gerade denke, mit einem Event die gleiche Logik ('Stopp mal kurz') hinzubekommen. Zum Thema 'threadsicheren Zugriff' gibt es viele Ansätze. Wie viele Threads wollen gleichzeitig lesen (dürfen)? Ist es mehr als einer, dann verwende den TMultipleReadExclusiveWrite-Dingsda aus der SyncObjs-Unit. Greift ein Thread vielleicht doch mehrfach auf die Resourcen zu ('Timer'?) Dann reicht eine TCriticalSection nicht. |
Re: Deadlock beim stoppen eines Arbeiter Threads
Hi,
also ich nutze jetzt ein Event, dass ich im Thread setzte wenn der Thread angehalten hat.
Delphi-Quellcode:
Nun mache ich folgendes in meinem Hauptthread:
while fShouldStopp do
begin fStoppEvent.SetEvent; // Hier wird das Event gesetzt!! //fWoBinIch:=6; Sleep(1); if Self.Terminated then break; if Application.terminated then break; end; end; fStoppEvent.ResetEvent; // Hier wird das Event wieder zurückgesetzt
Delphi-Quellcode:
PleaseStopp ist eine Prozedur des Threads und sieht folgendermaßen aus:
*******************************************************************************
Prozedur zum stoppen des Hintergrund Tasks *******************************************************************************) procedure TfrmMain.StoppBackgroundTask; begin if Assigned(aCyclicDataThread) then begin aCyclicDataThread.PleaseStopp; WaitForSingleObject(aCyclicDataThread.fStoppEvent.Handle,INFINITE); // Warten auf das Event end; // of if Assigned(aCyclicDataThread) then end;// of procedure TFrmMain.StoppBackgroundTask; ////////////////////////////////////////////////////////////////////////////////
Delphi-Quellcode:
Sollte ich jetzt hier irgendwas umstellen???
procedure TCyclicData.PleaseStopp;
begin fSyncObj.Acquire; try fShouldStopp:=true; finally fSyncObj.Release; end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:31 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 by Thomas Breitkreuz