Laden...

WindowsForm friert ein, Threadübergreifender Zugriff auf Label

Erstellt von dodoflash vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.879 Views
Thema geschlossen
D
dodoflash Themenstarter:in
3 Beiträge seit 2011
vor 12 Jahren
WindowsForm friert ein, Threadübergreifender Zugriff auf Label

Hi,
Hoffe ich bin hier richtig.
Hab ein Problem mit einem Threadübergreifenden Zugriffe auf die Eigenschaft label.visible.

Ich verwende Aforge.net für die Wiedergabe einer mpg.datei. Über den newFrame-Event wird bei jedem neuen Bild das der videosourceplayer erhällt eine Algorithmus zur Bildbearbeitung ausgefürt.

Dieser Liefert einen Wert der je nach Höhe ein Label visible= true bzw. false setzt.

Da der direkte Zugriffe auf die Steuerelemente zu einer Exception führt. Habe ich mir ein paar Codeschnippseln zusammengesucht und das über delegaten geregelt.

Hier der Code:

New-Frame-Event von dem aus die Änderung des Steuerelements aufgerufen wird



private void videoSourcePlayer1_NewFrame(object sender, ref Bitmap image)
{
            ...........Algorithmus der count festlegt.......  

              if ((count >= DetectionValue) && (TrainDetected == false))
               {
                   TrainDetected = true;
                   Zugdetektion(true);
               }
               if ((count < DetectionValue) && (TrainDetected == true))
               {
                   TrainDetected = false;
                   Zugdetektion(false);
               }
               
               // Edit: Ohne diesen zweiten Aufruf schein alles zu funktionieren 
               WriteTextBox("Frames: " + Count);
}


Änderung des Steuerelements:


delegate void ZugdetektionDelegate(bool detected);
       private void Zugdetektion(bool detected)
       {
           if (label5.InvokeRequired)
           {
               label5.Invoke(new ZugdetektionDelegate(this.Zugdetektion), new object[] { detected });
           }
           else
           {
               if (detected)
                   label5.Visible = true;
               else
                   label5.Visible = false;
           }
       }


Zweite Änderung eines Steuerelements:


delegate void WriteTextBoxDelegate(string text);
       private void WriteTextBox(string text)
       {
           if (label3.InvokeRequired)
           {
               label3.Invoke(new WriteTextBoxDelegate(this.WriteTextBox), new object[] { text });
           }
           else
           {
               label3.Text = text;
           }
       }


Dabei kommt es zu folgendem Fehler:

Das Video und die gesamte Form friert ein und beim Versuch mit Pause im Debugger in den derzeitigen ausgeführten Code zu gelangen wird immer Application.Run(new Form1());
angezeigt. Breakpoints im Programmcode haben ebenfalls zu keinem Aufruf geführt sobald die Form eingefrohren ist.
Der Fehler passert immer exact nach der selben Anzahl von erhaltenen Frames. Geht man den Code ab dieser Zahl händisch mit Breakpoints durch kommt es zu keiner Fehlermeldeung und der Code wird normal ausgeführt. Sobald man das Programm dann allerdings wieder laufen lässt friert wieder alles ein.

Edit: Also scheint so als ob die zwei Aufrufe auf die labels sich gegenseitig irgendwie stören und den freeze auslösen. Wenn nur eins von beiden verwendet wird funktionierts einwandfrei.

Wäre nett wenn wer einen Rat hätte was hier falsch rennt.

Danke und lg
dodoflash

916 Beiträge seit 2008
vor 12 Jahren

Hallo dodoflash,

und willkommen im Forum.

Ich vermute mal du knallst die Windows MessageQueue voll. Das heißt du Invokes zu viel, und der GUI Thread ist ständig damit beschäftigt die GUI Zugriffe auszuführen. Versuch mal mit BeginInvoke statt Invoke zu arbeiten, bzw. solltest du dir überlegen, ob es Sinnvoll ist die UI so oft upzudaten. Das kann das menschliche Auge gar nicht wahrnehmen 😄 Ich denke all 10 Frames reicht auch zu.

Again what learned...

D
dodoflash Themenstarter:in
3 Beiträge seit 2011
vor 12 Jahren

Okay werd das mal versuchen.

Zu oft wird eh nicht upgedated, immer nur wenn sich der Status ändert wird zw. visible true und false und das ist eigentlich nur 2 bis 3 mal wenn alles funktioniert für ein file (ca. 1min) ansonst bleibt das label gleich.

Das zweite label gibt nur die anzahl der Frames an, und auch das ist eigentlich nicht wirklich flott durch den algorithmus davor gibts nur ca alle 700ms eine Änderung und man sieht das schon mit freiem Auge.

Aber ich werd das mit dem BeginInvoke mal versuchen. Thx

916 Beiträge seit 2008
vor 12 Jahren

Ob das nur 2-3 mal geändert wird ist die eine Frage, jedoch so wie ich das sehe, machst du trotzdem nach jedem Frame ein GUI zugriff via Invoke. Was dann dort drin passiert, bzw. ob dort in dem delegaten überhaupt was passiert ist eine andere Frage, aber die MessageQueue müllst du dennoch voll!

Again what learned...

D
dodoflash Themenstarter:in
3 Beiträge seit 2011
vor 12 Jahren

Okay also mit BeginInvoke funktioniert es einwandfrei thx.
Werd mir aber auch noch den Link anschauen den ich geschickt bekommen habe.

[FAQ] Warum blockiert mein GUI?

Danke für die schnellen Antworten.

U
1.688 Beiträge seit 2007
vor 12 Jahren

Hallo,

ich würde die Ursache eher woanders suchen. Hast Du weitere Threads und/oder Timer, die auf die GUI zugreifen?

Die Aktualisierung von zwei Labels (2x Invoke nacheinander) kann nicht zu einem Deadlock führen - es würde höchstens etwas langsamer und ruckelig werden. Allerdings verdoppelt sich natürlich die Wahrscheinlichkeit für das Auftreten des Problems an anderer Stelle.

BeginInvoke kann natürlich das Problem erstmal entschärfen, da es nicht blockiert, aber die Ursache sollte man trotzdem verstehen und nicht einfach solang herumbasteln bis es geht (sinngemäßes Zitat von herbivore).
Übrigens sollte man ja meinen, dass die "Message queue" bei BeginInvoke mehr beansprucht wird als bei Invoke, denn Invoke kehrt erst dann zurück, wenn ein Eintrag bearbeitet wurde.

Noch ein paar Bemerkungen:

  • [Tipp] Anfängerfehler == true / == false
  • Die Abfrage auf "InvokeRequired" ist unnötig, da der Aufruf sicher aus einem Nicht-GUI-Thread erfolgt
  • und natürlich ist es nicht optimal, für jede einzelne Update-Aktion ein eigenes Invoke zu benutzen (Zugdetektion und WriteTextBox)
916 Beiträge seit 2008
vor 12 Jahren

Über den newFrame-Event wird bei jedem neuen Bild das der videosourceplayer erhällt eine Algorithmus zur Bildbearbeitung ausgefürt.

Die Aktualisierung von zwei Labels (2x Invoke nacheinander) kann nicht zu einem Deadlock führen

Ich denke wenn man 2 Labels 100 mal in der Sekunde updated dann kann das schon zu einem deadlock führen. Weil wie ich schon erwähnt habe Invoked er die sachen immer, und checked nicht mit einem flag ob das Label sowieso schon visible ist. Das Visible Flag selber darf er ja nicht abfragen da auch lesende Zugriffe auf GUI Elemente außerhalb des GUI Threads vermieden werden sollten.

Dennoch gebe ich ujr recht, das es eventuell noch eine andere Ursache für das Verhalten geben wird, als nur die beiden Label Invokations. Bezüglich der vielen verschiedenen Delegaten, schau dir auch mal Action delegate (.Net 4.0) bzw. das Func<T, TResult> (.Net 4.0) an.

Again what learned...

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo rollerfreak2,

Control.Invoke arbeitet in Gegensatz zu Control.BeginInvoke synchron. Daher ist es bei Control.Invoke im Gegensatz zu Control.BeginInvoke unmöglich, dass man "die Windows MessageQueue voll knallt". Durch Control.Invoke steht (pro Thread) zu jedem Zeitpunkt maximal ein Eintrag in der MessageQueue. Nur mit Control.BeginInvoke kann man die "die Windows MessageQueue voll knallen".

Trotzdem kann man das GUI mit unnötig häufigem Aufrufen sowohl von Control.BeginInvoke als auch von Control.Invoke de facto zum Erliegen bringen, weshalb man die Zahl der Aufrufe (pro Sekunde) gering halten sollte.

Aber das und alles andere hier im Thread steht auch schon in dem von dodoflash geposteten Link und seinem "Bruder" [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke).

herbivore

Thema geschlossen