AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Deadlock beim stoppen eines Arbeiter Threads

Ein Thema von DelphiManiac · begonnen am 10. Mär 2007 · letzter Beitrag vom 13. Mär 2007
Antwort Antwort
Seite 1 von 2  1 2      
DelphiManiac

Registriert seit: 5. Dez 2005
742 Beiträge
 
#1

Deadlock beim stoppen eines Arbeiter Threads

  Alt 10. Mär 2007, 20:37
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..

aThread.Suspend; Kommt nicht Frage, da ich nie genau weiß in welchem Zustand mein Schnittstellenobjekt gerade ist(könnte ja sein, dass er gerade gesendet hat)..

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:
(*******************************************************************************
  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;
////////////////////////////////////////////////////////////////////////////////
Könnt ihr mir helfen, wie ich das besser lösen kann??

Danke euch
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.063 Beiträge
 
Delphi 12 Athens
 
#2

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 10. Mär 2007, 21:13
[edit]
hab leider editiert, statt zitiert ... also Beitrag is futsch und leider auch nicht mehr in meiner Cache auffindbar
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
DelphiManiac

Registriert seit: 5. Dez 2005
742 Beiträge
 
#3

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 10. Mär 2007, 22:13
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
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#4

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 10. Mär 2007, 22:45
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.

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.
Angehängte Dateien
Dateityp: zip csworkerthread_190.zip (1,2 KB, 16x aufgerufen)
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.063 Beiträge
 
Delphi 12 Athens
 
#5

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 11. Mär 2007, 16:59
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:
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;
im Thread dann z.B. 2 Variablen: fShouldStopp und isStopped
(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

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.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
DelphiManiac

Registriert seit: 5. Dez 2005
742 Beiträge
 
#6

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 12. Mär 2007, 10:30
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
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#7

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 12. Mär 2007, 10:44
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.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
DelphiManiac

Registriert seit: 5. Dez 2005
742 Beiträge
 
#8

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 12. Mär 2007, 10:54
@alzaimar:
Hi,
du schreibst:
Zitat:
(weil keine 'offiziellen' Synchronisationsmechanismen verwendet werden)
was meinst du mit 'offiziell', wie soll ich denn den Zugriff auf den Bereich synchron halten, so,
dass immer ein atomarer Zugriff auf meine Variablen erfolgt?

Danke dir!
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#9

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 12. Mär 2007, 11:11
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.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
DelphiManiac

Registriert seit: 5. Dez 2005
742 Beiträge
 
#10

Re: Deadlock beim stoppen eines Arbeiter Threads

  Alt 12. Mär 2007, 11:27
Hi,

also ich nutze jetzt ein Event, dass ich im Thread setzte wenn der Thread angehalten hat.

Delphi-Quellcode:
      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
Nun mache ich folgendes in meinem Hauptthread:

Delphi-Quellcode:
*******************************************************************************
  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;
////////////////////////////////////////////////////////////////////////////////
PleaseStopp ist eine Prozedur des Threads und sieht folgendermaßen aus:

Delphi-Quellcode:
procedure TCyclicData.PleaseStopp;
begin
  fSyncObj.Acquire;
  try
    fShouldStopp:=true;
  finally
    fSyncObj.Release;
  end;

end;
Sollte ich jetzt hier irgendwas umstellen???
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:25 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz