Probleme beim Deadlock-Entfernen
-
- 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
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...)
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.
Re: Deadlock "finden"
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.
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.
-
- 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"
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
)
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

Re: Deadlock "finden"
Es gibt eine brandneue Implementation von TMultiReadExclusiveWriteSynchronizer, vielleicht hilft das?
Ich glaub's zwar eher nicht, finde aber das Wort ganz lustig!
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;
Ich glaub's zwar eher nicht, finde aber das Wort ganz lustig!

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;
-
- 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"
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
Den haben sich aber auch die Borländer ausgedacht 
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


Re: Deadlock "finden"
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.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)
Ist das nicht anders lösbar (testweise evtl)?
-
- 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"
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.
Es ist auch völlig egal, ob in der CriticalSection nur ein writeLn() oder eine komplexere Anweisung steht - das Resultat ist gleich.
Re: Deadlock "finden"
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?

Kann es etwa sein, dass du zuviele Threads bastelst und das irgendwann aus Memory Gründen nicht mehr will?
-
- 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"
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!)
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!)
Re: Deadlock "finden"
Na, so einfach ist das afaik nicht. Da kann schneller mal Schluss sein.Targion hat geschrieben:Bei 1GB Speicher?
S.a. http://bugs.freepascal.org/view.php?id=13105" onclick="window.open(this.href);return false;
- 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"
Es gibt aussser dem Speicher auch noch andere Systemresourcen. Semaphoren, Mutex und CriticalSections gehören auch dazu.
Beziehen sich die CriticalSections auf dasselbe Syncobject ?
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).
-
- 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"
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.af0815 hat geschrieben:Beziehen sich die CriticalSections auf dasselbe Syncobject ?
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...
-
- 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
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;