Laden...

Kann hier ein Deadlock entstehen?

Erstellt von mika92 vor 14 Jahren Letzter Beitrag vor 14 Jahren 2.831 Views
M
mika92 Themenstarter:in
12 Beiträge seit 2009
vor 14 Jahren
Kann hier ein Deadlock entstehen?

Hallo liebe Community,
hab in eurem Board schon oft hilfreiche Tipps gefunden und melde mich nun auch mal selbst zu Wort =)
Und zwar ginge es um folgenden Code:


public void Attach(ClockObserver o)
        {
            lock(_lockThis)
            {
                _attachedObservers.Add(o);
            }
        }

        public void Detach(ClockObserver o)
        {
            lock (_lockThis)
            {
                _attachedObservers.Remove(o);
            }
        }

        public void FireUpdates(object drawInfo)
        {
            var e = (DrawInformations)drawInfo;

            lock (_lockThis)
            {                
                foreach (ClockObserver o in _attachedObservers)
                    o.Update(e);
            }
        }

Und zwar wird Attach beim Öffnen einer Form, Detach beim Schließen, und FireUpdates automatisch jede Sekunde aufgerufen.
Das Update führt zum Zeichnen einer selber geschriebenen Komponente.

Auf jeden Fall hängt sich mein Programm ab und zu auf, wenn ich eine Form schließe.
Jetzt ist meine Frage, sind meine lock's eventuell falsch gesetzt, kann man das aus diesem kleinen Codestück ablesen?

Danke schon im Voraus.

Greets
Mika

3.170 Beiträge seit 2006
vor 14 Jahren

Hallo,

Auf jeden Fall hängt sich mein Programm ab und zu auf, wenn ich eine Form schließe. Machst Du die Updates über einem Timer?

  • Wenn ja, diesen stoppen
  • alle ClockObserver detachen
    wenn die Form schließt.
    Wenn das nix bringtwäre dann evtl. der Code nötig, den Du beim Schließen des Fensters benutzt.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

179 Beiträge seit 2006
vor 14 Jahren

Hallo,

Kann es sein, dass du in der Update - Funktion der Form ein Invoke verwendest um dich dort mit dem GUI-Thread zu Synchronisieren?

Wenn ja, kann folgendes Szenario entstehen:

Thread A ... Gui - Thread.
Thread B ... Thread der Fire Updates aufruft.

  • B ruft Fire Updates auf
  • Irgendwann zwischen lock (Zeile 21) und Aufruf der Update - Funktion passiert ein Threadwechsel
  • Form soll geschlossen werden und Thread A ruft Detach auf (kommt bis zu lock (Z.11) und wartet ab hier)
  • Update wird aufgerufen (Thread B).
  • Mit Invoke synchronisierung auf Thread A
  • Thread A warted auf freigabe des lock, Thread B warted auf beendigung von Invoke. A wartet auf B und B wartet auf A --> Deadlock

greets

M
mika92 Themenstarter:in
12 Beiträge seit 2009
vor 14 Jahren

Kann es sein, dass du in der Update - Funktion der Form ein Invoke verwendest um dich dort mit dem GUI-Thread zu Synchronisieren?

Ja, ich lasse in der Update-Methode über Invoke mein Control refreshen, damit es neu gezeichnet wird (dauert zwischen 0 und 2 Millisekunden, das heißt, wenn jede Sekunde aktualisiert wird, darf es bei 50 Controlls kein Problem von wegen zu langer Refresh-Dauer geben) ...

Das heißt der Fehler rührt vom Invoke...

Jetzt wäre es noch irgendwie schön, wenn du vielleicht eine Lösung parat hättest xD
Weil ich eben noch eher neu bin in Multithreadprogrammierung und Threadsicherheit.

Greets und Danke
Mika

179 Beiträge seit 2006
vor 14 Jahren

versuch mal statt Invoke mit BeginInvoke zu synchronisieren, da dies den aufrufenden Thread nicht blockiert.

Ich bin mir jetzt allerdings nicht 100%ig sicher, ob dies nicht wieder zu anderen Threadingprobs. durch das Schließen der Form kommen kann, da noch ein Update nach dem Schließen kommen kann.

M
mika92 Themenstarter:in
12 Beiträge seit 2009
vor 14 Jahren

versuch mal statt Invoke mit BeginInvoke zu synchronisieren, da dies den aufrufenden Thread nicht blockiert.

Ich bin mir jetzt allerdings nicht 100%ig sicher, ob dies nicht wieder zu anderen Threadingprobs. durch das Schließen der Form kommen kann, da noch ein Update nach dem Schließen kommen kann.

Du bist mein Held =)
Mit BeginInvoke gehts...

        
        public void Update(DrawInformations e)
        {
            drawInformations = e;
            if (this.InvokeRequired)
                this.BeginInvoke(new UpdateMethod(this.Update), e);
            else
                this.Refresh();
        }

Aber wie kann ich mir das mit BeginInvoke jetzt genau vorstellen? Der wartet dann also wieder sozusagen als eigenständiger Teil darauf, dass die Threads synchronisiert sind und blockiert nicht? Und ruft dann, wenn sie synchron sind, die Methode eben erneut auf...

Aber wenn in der Zeit, in der er nun darauf wartet, dass sie synchronisiert sind, könnte doch nun das Element gelöscht werden oder so, dann bekomme ich höchstwahrscheinlich eine ObjectDisposedException, oder?
EDIT: Okay, diesen Fehler hatte ich früher... Habe nun einfach einige Male ca. 50 Forms geöffnet und nach der Reihe geschlossen und konnte diese Exception nicht mehr beobachten. Ich hoffe es hat sich damit nun erledigt. =)
EDIT2: Habe nun alles durchdacht, hatte früher die lock's in Attach und Detach noch nicht. Wenn ich nun das Fenster schließe, rufe ich Detach mit seinen enthaltenen Controls auf. Damit ist eigentlich gewährleistet, dass sie entweder gleich gelöscht werden, oder sie so lange warten, bis alle neu gezeichnet wurden und dann gelöscht wurden. Also alles im grünen Berreich.

Ich bedanke mich noch einmal recht herzlich.

Greets Mika

179 Beiträge seit 2006
vor 14 Jahren

Schau mal unter [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke), dort ist das mit Invoke & BeginInvoke schön beschrieben, auch warum es bei Invoke zu einem Deadlock kommen kann.

Der Hauptunterschied ist, dass Invoke warted bist die Aktion im Gui-Thread abgeschlossen ist, und BeginInvoke dem Gui-Thread quasi sagt, mach mal die Aktion wenn du Zeit hast, und sofort weitermacht, ohne auf die Abarbeitung zu warten.

M
mika92 Themenstarter:in
12 Beiträge seit 2009
vor 14 Jahren

Danke, so umrisshaft wusste ich das meiste schon, zumindest der Hintergrund, warum Invoke verwendet werden muss.
Aber BeginInvoke war mir noch nicht bekannt.
Danke dir.

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo mika92,

warum verwendest du statt der Attach/Detach/FileUpdate-Mimik nicht besser ein eigenes Event? Siehe [FAQ] Eigenen Event definieren

herbivore

M
mika92 Themenstarter:in
12 Beiträge seit 2009
vor 14 Jahren

warum verwendest du statt der Attach/Detach/FileUpdate-Mimik nicht besser ein eigenes Event? Siehe
>

Aufgabe des Lehrers... Einmal nicht mit Events programmieren, sondern mal eher so wie in Java... Dadurch wird anscheinden "die Bindung auch nicht so stark"... Naja wenn er meint xD


Sorry, aber noch einmal zurück zu meinem Programm... Mir ist nun doch eine Schwachstelle aufgefallen.

Gehen wir nun davon aus, dass mein GUI-Thread grad ein bisschen beschäftigt ist und er auf mein BeginInvoke nicht sofort reagiert...
Angenommen ich schließe nun meine Form (dabei rufe ich Detach auf und entferne meine Componente von der Update-Liste, Invoke wurde aber noch immer nicht gemacht)... Und dann räumt der GarbageCollector meine Componente auf (ruft dabei Dispose auf, oder)... Und nun käme es zum Invoke... dann wäre ja mein Objekt disposed... sollte ich da ein try-catch über meine gestamte Update-Methode einbauen?

Greets
Mika

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo mika92,

es ist natürlich besser zu verhindern, dass eine ObjectDisposeException auftritt, als sie wegzufangen.

Dazu sagt man im FormClosing dem Thread Bescheid, dass er sich beenden soll und bricht das Schließen erstmal ab. Erst wenn der Thread (in der letzten Anweisung) dem Form sagt, dass er fertig ist, schließt man das Form endgültig.

herbivore

M
mika92 Themenstarter:in
12 Beiträge seit 2009
vor 14 Jahren

Aber woher weiß ich, was genau die letzte Zeile meines GUI-Threads ist?
Sry, die Frage kommt jetzt vielleicht ein bisschen blöd.
Das mit dem Schließen abbrechen sollte ich aus dem Gedächtnis heraus noch schaffen.

Aber in meinem FormClosing führe ich doch nur den einen Befehl aus... und es kann eben bei einem ganz blöden zufall sein, dass dann noch ein invoke folgt... und das müsste ich abfangen und habe keine plan wie... gg

Greets
Mika

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo mika92,

Aber woher weiß ich, was genau die letzte Zeile meines GUI-Threads ist?

die letzte Zeile des Worker-Threads. Zuerst muss der Worker beendet werden. Dann kann man das Fenster schließen und dann erst den GUI-Thread.

und es kann eben bei einem ganz blöden zufall sein, dass dann noch ein invoke folgt...

Nein, bei dem von mir beschrieben Vorgehen kann das nicht sein.

herbivore