Probleme beim Deadlock-Entfernen

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Probleme beim Deadlock-Entfernen

Beitrag von Targion »

Hallo!
Ich habe hier ein Programm, welches über 100 Threads verwaltet. Je nach Rechner und Eingabe kommt es innerhalb dieser Threads zu einem Deadlock, was sich darin äußert, dass das Programm einfriert.
Die Frage ist: Wie finde ich die Problemstelle im Code? Gibt es da Werkzeuge für, oder muss ich raten? (obwohl ich schon eine Idee habe, wo das Problem sein könnte...)
Zuletzt geändert von Targion am Do 4. Feb 2010, 00:03, insgesamt 1-mal geändert.

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Deadlock "finden"

Beitrag von theo »

Mir ist kein Werkzeug bekannt (was nichts heissen will). Aber ich würde sagen, dass man diese meist durch logisches analysieren finden kann.
Irgendwo muss wohl eine Kondition sein, wo mehrere Threads auf die gleichen zwei Was-auch-immer warten können.
http://de.wikipedia.org/wiki/Deadlock" onclick="window.open(this.href);return false;

Vllt. Helfen auch erstmal ein paar writelns "Thread x entering condition blah", "Thread x waiting for resource blah" oder so.

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Deadlock "finden"

Beitrag von Targion »

Ich habe jetzt ein sehr komisches Problem: ich benutze gerne den Nemiver Debugger, wenn die Programme etwas größer werden und aus mehreren Modulen bestehen. Also habe ich den mal auf das Projekt losgelassen. Als der Deadlock dann kam, konnte ich den GDB nicht zum stop bewegen. Also wollte ich das Programm killen, dabei hat sich dann die komplette X11-Umgebung aufgehangen. Nur über ein direktes Kommando (aus dem kernel) konnte das programm dann beendet werden.

Führt es zu einem Deadlock, wenn eine Gruppe von Daten in einem Synchronize in einer CriticalSection geschrieben wird und gleichzeitig versucht wird, auf diese lesend zuzugreifen und das in einem Thread? (dem Main-Thread) Ich bin mir noch nicht ganz darüber klar geworden, ob das überhaupt möglich ist (Synchronize wird ja im Kontext des Hauptthreads ausgeführt), aber ansonsten sehen ich keinen Grund, warum die GUI des Programmes einfrieren soll, der Rest aber weiterläuft. (das merkt man am CPU-Verbrauch :-P)

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Deadlock "finden"

Beitrag von theo »

Es gibt eine brandneue Implementation von TMultiReadExclusiveWriteSynchronizer, vielleicht hilft das?
Ich glaub's zwar eher nicht, finde aber das Wort ganz lustig! :lol:
Es ist aber nicht nur ein Witz, im Grunde passt das nicht schlecht zu deinem Vorhaben.
Neueste Quelle:
http://svn.freepascal.org/cgi-bin/viewv ... ortby=date" onclick="window.open(this.href);return false;

http://www.freepascal.org/docs-html/rtl ... nizer.html" onclick="window.open(this.href);return false;

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Deadlock "finden"

Beitrag von Targion »

Ich habe ja schon die fehlerquelle gefunden... Nur den Grund dafür verstehe ich nicht... Das Programm hängt bei einem EnterCriticalSection() im Hauptthread. Das kann nur dann passieren, wenn die CriticalSection gerade betreten ist und nicht freigegeben wird.
In jedem Thread, der CriticalSections verwendet, wird die CriticalSection aber mit LeaveCriticalSection() in einem Finally-Block wieder verlassen...
Warum gibt es da also Probleme? (Der einsprung in die Critical Section passiert in einem Synchronize, das sollte aber kein Problem sein)

P.S: TMultiReadExclusiveWriteSynchronizer - der Name gehört auch zu meinen Favoriten :-P Den haben sich aber auch die Borländer ausgedacht :roll:

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Deadlock "finden"

Beitrag von theo »

Targion hat geschrieben: Warum gibt es da also Probleme? (Der einsprung in die Critical Section passiert in einem Synchronize, das sollte aber kein Problem sein)
Ohne genaueres zu wissen, würde ich rein gefühlsmässig das Problem aber doch am ehesten in den Verschachtelung von CS in Synchronize vermuten.
Ist das nicht anders lösbar (testweise evtl)?

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Deadlock "finden"

Beitrag von Targion »

Ohne das Synchronize kommt der gleiche Effekt: Erst läuft alles normal, aber irgendwan springen stoppen die Threads reihenweise an EnterCriticalSection() und nichts geht mehr.
Es ist auch völlig egal, ob in der CriticalSection nur ein writeLn() oder eine komplexere Anweisung steht - das Resultat ist gleich.

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Deadlock "finden"

Beitrag von theo »

Tja, was soll man sagen. Entweder treten wir nun ins Reich von Alice in Wonderland ein, oder du machst etwas falsch ;-)
Kann es etwa sein, dass du zuviele Threads bastelst und das irgendwann aus Memory Gründen nicht mehr will?

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Deadlock "finden"

Beitrag von Targion »

Bei 1GB Speicher? Das Programm frist schon ordentlich was, aber noch reicht die Menge an RAM... Ich habe den Aufruf jetzt an eine Andere Stelle bewegt: Schon seit 5 Minuten läuft alles gut. Es kann also sein, dass das Problem dadurch zustande kommt, dass EnterCriticalSection() zu schnell nach LeaveCriticalSection() aufgerufen wurde - was aber auch irgendwie ein blödsinniger Grund ist, nach und nach alle Threads aufzuhängen.
Zudem bekomme ich jetzt Meldungen von wegen "QPainter ended with 2 saved states.". das passiert eigentlich, wenn zwei Threads gleichzeitig versuchen, auf das Canvas der Anwendung zu zeichnen - dazu hat aber mit 100%iger Sicherheit nur der Hauptthread die Möglichkeit, welcher sich timergesteuert die Daten all 500msec holt. (Die Anweisung in der problematischen in der CriticalSection überweist eben diese Daten (hauptsächlich Listenstrukturen und Int64) an den Hauptthread zum zeichnen)
Das Ganze ist irgendwie komisch... Noch dazu muss diese Anwendung später auf einem Server laufen und darf sich in keinem Fall aufhängen... Also ich bin schon im Wunderland, bleibst du draußen oder nimmst du auch einen Schluck aus der Flasche? (bitte nicht!)

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Deadlock "finden"

Beitrag von theo »

Targion hat geschrieben:Bei 1GB Speicher?
Na, so einfach ist das afaik nicht. Da kann schneller mal Schluss sein.
S.a. http://bugs.freepascal.org/view.php?id=13105" onclick="window.open(this.href);return false;

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6770
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Deadlock "finden"

Beitrag von af0815 »

Es gibt aussser dem Speicher auch noch andere Systemresourcen. Semaphoren, Mutex und CriticalSections gehören auch dazu.

Beziehen sich die CriticalSections auf dasselbe Syncobject ?
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Deadlock "finden"

Beitrag von Targion »

af0815 hat geschrieben:Beziehen sich die CriticalSections auf dasselbe Syncobject ?
Ja. Es gibt so ca. 240 Threads, die alle mit dem einen Thread syncen. In diesem Zusammenhang existiert der Kritische Abschnitt. Komischerweise funktioniert das Programm jetzt, nachdem ich den Aufruf verschoben habe halbwegs wie es soll... Aber ich traue dem Frieden kein bisschen.
Zudem läuft das Programm nur mit halber Last (die Threads sind Teil einer Biosimulation), wenn ich die Berechnungen aufwändiger mache, kann es schon wieder zu Problemen kommen. Und dann ist da noch die komische Sache mit dem QPainter, die ich sehr unlogisch finde...

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Probleme beim Deadlock-Entfernen

Beitrag von Scotty »

Mir ist schleierhaft, warum du CS und Synchronize zusammen erwähnst, die haben doch nichts miteinander zu tun. Ansonsten kann ich empfehlen, die Anwendung unter Windows zu testen. Mein Threading unter Linux ist äußerst robust und ich kann Dinge tun, die unter Windows sofort zum Absturz führen. Wenn ich bei meinen Threads Synchronize aufrufe, friert der Thread übrigens ein. Möglicherweise ist das nicht sauber implementiert von FPC.

Code: Alles auswählen

type 
  TOnAddBest = procedure(...) of Object;
 
  TSingleOperation=class(...)
    public
       procedure Run;
    end;
 
  TBruteForce=class
      FProcessing   : TList; //list of TSingleOperation
      FMaxThreads : integer;//assigned by GetSystemThreadCount() of MTProcs ->MTCPU
      procedure SetupProcessing;//fills FProcessing
      procedure RunThreaded(aStart:word=0);
      procedure AddBest(...);
    public
      function ComputeMove:boolean
    end;
 
  TSingleThread=class(TThread)
    private
      FOperation : TSingleOperation;
    protected
      procedure Execute; override;
    public
      constructor Create(aOperation:TSingleOperation);
    end;
 
implementation
 
var 
  {$ifdef CPU32}
    aCriticalSection:LongWord;
  {$else}
    aCriticalSection:QWord;
  {$endif}
  ThreadsRunning : integer;
 
{ --- TBruteForce --- }
 
procedure TBruteForce.RunThreaded(aStart: word);
begin
  if aStart=0 then ThreadsRunning:=0;
  if aStart<FProcessing.Count then
  begin
    with TSingleThread.Create(TSingleOperation(FProcessing[aStart])) do
    try
      Resume;
      if ThreadsRunning>FMaxThreads then Waitfor;
{      while not Terminated do
      begin
        if ThreadsRunning<FMaxThreads then RunThreaded(aStart+1);
        Sleep(10);
      end;
}      OnProgress(nil,round((aStart/FProcessing.Count)*100));
      Application.ProcessMessages;
      RunThreaded(aStart+1);
    finally
      Free;
    end;
  end;
end;
 
function TBruteForce.ComputeMove:boolean;
begin
  FMaxThreads:=GetSystemThreadCount;
  SetupProcessing;
  ...
  if fmGameOptions.cbParallel.Checked then          //run parallel
  begin
    InitializeCriticalSection(aCriticalSection);
    try
      RunThreaded;
    finally
      DeleteCriticalSection(aCriticalSection);
    end;
  end else
  begin                                                           //run single threaded
    for i:=0 to FProcessing.Count-1 do
    begin
      OnProgress(self,round((i/FProcessing.Count)*100));
      Application.ProcessMessages;
      TSingleOperation(FProcessing[i]).Run(nil);
    end;
  end;
  ...
end;
 
{ --- TSingleOperation --- }
 
procedure TSingleOperation.Run(aThread:TThread);
begin
  ... //loops and calculations
  if aThread<>nil then EnterCriticalsection(aCriticalSection);
//  if aThread<>nil then aThread.Synchronize(aThread,@SynchMethod); //fails
  try
    if assigned(FOnAddBest) then FOnAddBest(...); //data exchange
  finally
    if aThread<>nil then LeaveCriticalSection(aCriticalSection);
  end;
  ...
end;
 
{ --- TSingleThread --- }
 
constructor TSingleThread.Create(aOperation: TSingleOperation);
begin
  inherited Create(true);
  FOperation:=aOperation;
end;
 
procedure TSingleThread.Execute;
begin
  inc(ThreadsRunning);
  FOperation.Run(self);
  dec(ThreadsRunning);
end;

Antworten