AGB  ·  Datenschutz  ·  Impressum  







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

Wieso bekomme ich hier einen Deadlock?

Ein Thema von Zacherl · begonnen am 24. Mai 2010 · letzter Beitrag vom 26. Mai 2010
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#1

Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 18:59
Hey,

ich habe hier ein etwas anspruchsvolleres Problem: Ich habe einen Thread, welcher praktisch dauerhaft (mit Sleep(10) unterbrochen) auf eine ThreadList zugreift (LockList und UnlockList in jedem Zyklus). Nun habe ich eine Benutzerfunktion, die von der VCL Oberfläche aus ebenfalls auf die ThreadList zugreift. Dies geschieht sehr selten.
Der Thread selbst ruft bei jedem Durchgang mehrmals Ereignissfunktionen, in diesem Falle OnProgress, auf.

Soweit funktioniert alles wunderbar. Die Synchronisation der Threads arbeitet gut und durch das Sleep findet auch nach jedem Zyklus ein Context Switch statt. Jetzt möchte ich aber im OnProgress Event eine Fortschrittsanzeige aktualisieren. Da das Event im Context des Threads läuft, muss ich hier mit der VCL synchronisieren. Hierzu benutze ich der Einfachheit halber die AsyncCalls Unit (allerdings auch mit Synchronize() des Threads getestet).

Okay der Thread läuft nun und der Fortschritt wird auch wunderbar angezeigt. ABER: Rufe ich jetzt die Benutzerfunktion auf, kommt es zum Deadlock, weil LockList komischerweise bis in alle Ewigkeiten auf die Synchronisation wartet. Dieser Fehler tritt wie gesagt nur auf, wenn ich im Event mit der VCL synchronisiere.

Momentan habe ich das Problem jetzt so behoben, dass ich vor Aufruf des Events aus dem Thread heraus UnlockList aufrufe und nach dem Event direkt wieder LockList. Das funktioniert zwar, mich würde aber trotzdem interessieren warum es anders zum Deadlock kommt.

Viele Grüße
Zacherl
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#2

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 19:05
Also in deinem Quelltext kann ich keinen Fehler sehen
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 19:14
Hier mal ein paar Teile Code. Der besagte Thread:
Delphi-Quellcode:
procedure TdxIDTPSendThread.Execute;
var
  I, X, Y: Integer;
  List: TList;
  Transfer: TdxIDTPTransfer;
begin
  while (not Terminated) do
  begin
    List := FWriter.TransferList.LockList;
    try
      X := 0;
      Y := 0;
      for I := 0 to List.Count - 1 do
      begin
        //
        // !! Code gekürzt: An dieser Stelle wird das OnProgress Event aufgerufen !!
        //
        if (not Transfer.Priority) then
        begin
          Inc(X);
        end;
        Inc(Y);
      end;
      // Beendete Transfers entfernen
      for I := List.Count - 1 downto 0 do
      begin
        Transfer := TdxIDTPTransfer(List[I]);
        if (Transfer.TransferState = tsFinished) then
        begin
          List.Delete(I);
          Transfer.Free;
        end;
      end;
    finally
      FWriter.TransferList.UnlockList;
    end;
    Sleep(10);
    // Thread suspendieren
    if (Y = 0) then
    begin
      {$WARNINGS OFF}
      Suspend;
      {$WARNINGS ON}
    end;
  end;
end;
Die Funktion, die von der GUI aus aufgerufen werden kann:
Delphi-Quellcode:
function TdxIDTPWriter.SendFile(Filename: String; Offset: Int64;
  TransferCode: Word; Encrypt: Boolean; const Compress, Priority: Boolean;
  const CreateSuspended: Boolean;
  const PacketSize: TdxPacketSize): TdxIDTPTransfer;
var
  List: TList;
begin
  Result := TdxIDTPFileTransfer.Create(FSendThread, Filename, Offset,
    GenerateTransferID, TransferCode, Priority, Encrypt, Compress, PacketSize);
  List := FTransferList.LockList;
  try
    //
    // !! Hier kommt es zum Deadlock !!
    //
    List.Add(Pointer(Result));
  finally
    FTransferList.UnlockList;
  end;
  if (CreateSuspended) then
  begin
    Result.Suspend;
  end else if (FSendThread.Suspended) then
  begin
    {$WARNINGS OFF}
    FSendThread.Resume;
    {$WARNINGS ON}
  end;
end;
Mein Code im Event:
Delphi-Quellcode:
var
  Item: TListItem;
begin
  AsyncCalls.EnterMainThread;
  try
    Item := TListItem(Transfer.Data);
    Item.SubItems[1] := IntToStr(Progress);
  finally
    AsyncCalls.LeaveMainThread;
  end;
Lasse ich den Code im Event beispielsweise ganz weg oder verzichte Testweise auf die Synchronisierung kommt es beim Aufruf von SendFile nicht zum Deadlock. Mit der Synchronisation allerdings schon.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 19:23
Kann zwar nichts entdecken, aber ein Deadlock entstecht z.B. durch sowas:

> in einem Thread wird die Liste gesperrt
> Verarbeitug wird im anderem Thread forgesetzt (Synchronize oder AsyncCalls)
> im anderem Thread soll ebenfalls die Liste gesperrt werden
(dieses geschieht niemals, da z.B. eine CriticalSection sich den sperrenden Thread merkt und somit hier auf die Freigabe im ersten Thread gewartet wird)
...
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#5

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 19:34
Ja deshalb war ich auch etwas verwundert, weil eigentlich ja alles funktionieren sollte. Kann mir höchstens vorstellen, dass das schnelle LockList und UnlockList im Thread da irgendwas durcheinanderbringt.
  Mit Zitat antworten Zitat
Benutzerbild von H4ndy
H4ndy

Registriert seit: 28. Jun 2003
Ort: Chemnitz
515 Beiträge
 
Delphi XE3 Professional
 
#6

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 20:25
Fuehre mal einen Lock-Counter in die Threadlist ein und schau einfach, was der fuer nen Wert hat. Bzw. einfach bei einem Wert > 1 ne Assertion einbauen oder ne Exception werfen o.ae.
Manuel
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#7

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 21:06
Okay die Exception vom Counter wird nie geworfen. Die ThreadList selbst scheint nicht das Problem zu sein. Der Code in meinem Progress Event war vorher so:
Delphi-Quellcode:
  Item := TListItem(Transfer.Data);
  TAsyncCalls.VCLSync(procedure
  begin
    Item := TListItem(Transfer.Data);
    Item.SubItems[1] := IntToStr(Round(Transfer.CurrentDataSize /
      Transfer.TotalDataSize * 100));
    Item.SubItems[2] := IntToStr(Round(100 - (Transfer.CompressedDataSize /
      Transfer.CurrentDataSize * 100)));
  end);
Das Problem war scheinbar, dass die Anzeige einfach zu schnell aktualisiert wurde (jede 8KiB). Hab dann jetzt zufällig den Code geändert, damit das ListView nicht mehr flackert und jetzt ist auch mein Deadlock weg Hier ist der neue Code:
Delphi-Quellcode:
Progress := Round(Transfer.CurrentDataSize / Transfer.TotalDataSize * 100);
  Item := TListItem(Transfer.Data);
  if (Integer(Item.Data) < Progress) then
  begin
    Item.Data := Pointer(Progress);
    TAsyncCalls.VCLSync(procedure
    begin
      Item := TListItem(Transfer.Data);
      Item.SubItems[1] := IntToStr(Round(Transfer.CurrentDataSize /
        Transfer.TotalDataSize * 100));
      Item.SubItems[2] := IntToStr(Round(100 - (Transfer.CompressedDataSize /
        Transfer.CurrentDataSize * 100)));
    end);
  end;
Vom verstehen dieses Problems bin ich zwar noch meilenweit entfernt, aber wenigstens funktioniert nun alles auch ohne meine eher quick'n'dirty Lösung. Danke euch allen
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.014 Beiträge
 
Delphi 12 Athens
 
#8

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 22:28
Wäre es nicht geschickter, den aktuellen Progress-Wert einfach in deinem Thread zu setzen und im Hauptthread entweder über OnIdle, ActionUpdate oder einen Timer-Event zu aktualisieren. Gegebenfalls musst du die Zugriffe auf den Progress-Wert noch durch einen TMultiReadExclusiveWriteSynchronizer absichern, was aber mit einer passenden Getter/Setter-Kombination transparent zu realisieren wäre.

Der Vorteil bei diesem Verfahren ist, daß der Thread von der Synchronisierung entlastet wird und der Hauptthread nur dann was aktualisiert, wenn er dazu auch Zeit hat.

Abgesehen davon ist das IMHO auch viel übersichtlicher.
Uwe Raabe
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#9

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 24. Mai 2010, 22:59
Für den Progress könnte ich es mit etwas Mühe so machen, das stimmt. Etwas Mühe deshalb, weil es sich um ein Protokoll handelt, welches simultane Transfer über ein einzelnes Socket realisiert (praktisch ein Multiplexer / Demultiplexer, wobei der Thread zum Multiplexer Teil gehört). Ich müsste also z.b. im Timer auch auf die ThreadList zugreifen, die alle Transfer Objekte enthält und dann alles iterieren. Somit würde beim Zugriff auf LockList auch wieder mit dem Thread synchronisiert.

Das eigentliche Problem sind aber eher die anderen Ereignisse, die man nicht so einfach ändern kann. Das Protokoll kann beispielsweise ganze Dateien verschicken. Dabei soll die Datei allerdings NICHT zuerst in den Speicher geladen und dann verschickt werden. Genauso gestaltet sich der Empfang. Es gibt ein OnTransferData() Event, welches von mir dafür gedacht wurde, die im aktuellen Paket empfangenen Daten in die entsprechende Datei zu schreiben.
Die Daten werden aus verschiedenen Gründen in 8 KiB Blöcke gestückelt. Zum einen natürlich, damit mehrere Übertragungen simultan laufen können und zum zweiten aufgrund einer "on the fly" Verschlüsselung und Kompression.
  Mit Zitat antworten Zitat
Benutzerbild von implementation
implementation

Registriert seit: 5. Mai 2008
940 Beiträge
 
FreePascal / Lazarus
 
#10

Re: Wieso bekomme ich hier einen Deadlock?

  Alt 25. Mai 2010, 08:15
Nehmen wir mal einen typischen Beispieldurchlauf:
1. (Thread) Liste wird gesperrt.
2. (VCL) Liste soll abgerufen werden, ist aber noch gesperrt -> es wird gewartet, bis die Liste wieder frei ist.
3. (Thread) Synchronisiert Event-Aufruf mit VCL-Thread.
4. Der VCL-Thread kann den Aufruf aber noch gar nicht synchronisieren, weil noch auf das Unlocken der Liste gewartet wird.

Und da ham wir den Deadlock. Beide Threads warten.

Edit: Oder hab ich da irgendwas falsch verstanden?
  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 10:10 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