Forum: PC-Programmierung C# Synchronisierung von Statusbalken


von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Hallo,

habe ein kleines Problem bzgl. WinForms in C#. Ich möchte einen 
Statusbalken auf meiner MainForm über einen Event triggern. Dieser Event 
wird von einem anderem Thread aufgerufen. Ich behandle das 
cross-threading-Ereignis mittels "this.Invoke". Invoke ruft dann, 
mittels eines Delegaten, mein "ProgressBar.PerformStep()" aus.

So weit alles dufte. Nur wird eben das grafische Umsetzen des 
Prozessbalkens nicht sofort ausgeführt. Da die Applikation etwas 
schneller läuft, kommt der Balken nur bis ca 80% wenn die Anwendung 
schon fertig ist. Füge ich "wait states" ein, bzw. führe ich 
debug-Einzelschritte aus, dann geht's.

Gibt es eine Möglichkeit das zu synchronisieren? Also das das Programm 
erst weiter läuft, wenn der doofe Balken sich erneuert hat? Das Programm 
ist nicht Zeitkritisch, somit wäre es nicht tragisch zu warten.

von Raik C. (raik_c)


Lesenswert?


von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

ok, danke erstmal. Also da ich ja synchron bleiben möchte, habe ich ja 
auch Invoke anstelle von BeginInvoke benutzt. Leider wird der Befehl 
zwar ausgeführt, bis es aber auf der GUI angekommen und gezeichnet ist, 
vergeht komischerweise sehr viel zeit...
Aus einem anderen Forum habe ich Folgendes:

"Yes, there can be an arbitrarily long delay. Invoke works by sending a 
Windows message to the target control, so it will only get processed 
when the target thread pumps messages. If the thread is already 
processing a message, and that processing takes time, then there may be 
an appreciable delay before the thread pumps its next message and 
thereby processes the Invoke."

von besucher (Gast)


Lesenswert?

Hi,

also an und für sich ist der Invoke-Aufruf synchron. Rein in den 
GUI-Thread, und der Event-Thread bleibt solange hängen, bis die 
Invocation fertig ist und zurückkehrt. Geht ja auch gar nicht anders, 
weil aus dem Invoke ein Returnwert rauspurzeln kann, der aus dem 
GUI-Thread stammt.

Das, was du beschreibst, deutet darauf hin, dass der Event-Thread nicht 
recycelt wird, sondern dass jeder Progress-Event auf einem neuen Thread 
kommt. Dann stauen sich die Threads in der Message-Queue der GUI. Du 
müsstest deinen Event-Werfer dazu bringen, single-threaded zu arbeiten.

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

und wie? haste ne idee?

von __tom (Gast)


Lesenswert?

die updates für den progessbar landen in der nachrichten warteschlange, 
du kannst das ab arbeiten der message-pump mit Application.DoEvents() 
veranlassen.

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

wo genau macht es sinn diesen befehl auszuführen? Mein momentaner 
ablauf:
thread triggered event auf form. der passende event handler auf der form 
sieht so aus:

1
      private void FlashProgressBar()
2
        {
3
            if (this.InvokeRequired)
4
            {
5
                this.EndInvoke(this.BeginInvoke(new MethodInvoker(delegate { progressBar2.PerformStep(); })));
6
            }        
7
        }

von __tom (Gast)


Lesenswert?

ich würde das so probieren:
1
private void FlashProgressBar()
2
        {
3
            if (this.InvokeRequired)
4
            {
5
                this.Invoke(new MethodInvoker(delegate { progressBar2.PerformStep(); }));
6
            }
7
            else
8
            {
9
                progressBar2.PerformStep();
10
            }
11
12
            Application.DoEvents()
13
        }

hinweise zu DoEvents():
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents.aspx

von besucher (Gast)


Lesenswert?

__tom schrieb:
> hinweise zu DoEvents():
> http://msdn.microsoft.com/en-us/library/system.win...

Hier vor allem den Caution-Absatz beachten:

Caution
Calling this method causes the current thread to be suspended while all 
waiting window messages are processed. If a message causes an event to 
be triggered, then other areas of your application code may execute. 
This can cause your application to exhibit unexpected behaviors that are 
difficult to debug.

Mit anderen Worten, das kann ordentlich ins Auge gehen. Das Problem ist, 
dass DoEvents alle wartenden Messages abarbeitet (auch die, die man 
nicht selbst erzeugt hat) und dass man keinen Einfluss darauf hat, 
welchen Rattenschwanz von Aktionen das hinter sich herzieht.

Peterle Anonym schrieb:
> und wie? haste ne idee?

Na ja, das kommt drauf an, wie das bisher programmiert ist. Ist es denn 
so, dass die Event-Quelle für jeden Event "new Thread" oder sowas macht 
und diesen dann den Event feuern lässt? Wenn ja, dann bau das mal aus 
und wirf den Event direkt aus dem Workerthread.

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

besucher schrieb:
> Na ja, das kommt drauf an, wie das bisher programmiert ist. Ist es denn
> so, dass die Event-Quelle für jeden Event "new Thread" oder sowas macht
> und diesen dann den Event feuern lässt? Wenn ja, dann bau das mal aus
> und wirf den Event direkt aus dem Workerthread.

new thread? Nein, das ganze sieht so aus:

Klassen:
MainForm.cs->I/O.cs

I/O.cs:
ein thread der ständig ein reveive bit pollt. wurde was empfangen, wird 
ein event getriggert:
1
pThreadWrite = new Thread(new ThreadStart(ReadThread));
2
pThreadWrite.Start();
3
...
4
5
private void ReadThread()
6
{
7
while(1)
8
{
9
    if (ReceiveEvent)
10
    {       
11
    FlashProgressBar();
12
    }
13
}
14
}

Der handler auf der main form:
1
     private void FlashProgressBar()
2
        {
3
            if (this.InvokeRequired)
4
            {
5
                this.EndInvoke(this.BeginInvoke(new MethodInvoker(delegate { progressBar2.PerformStep(); })));
6
            }        
7
        }


Ich weiss nun, dass es eindeutig ein timing problem ist. habe ein leeres 
projekt erstellt und die progressbar getriggert mit einem event, das ca 
alle 300 ms auslöst. bei erreichen von 100 werten, war die progress bar 
selbst erst auf ca 80%. sie kam einfach nicht hinterher und ihre 
bearbeitung (window refresh) hing noch in irgendeiner queue. Ich möchte 
das aber unbedingt synchronisiert haben!!!

von Markus V. (valvestino)


Lesenswert?

Da ja offensichtlich die GUI hinterherhinkt, wie wäre es denn, wenn Du 
nur jedes 10. (5., 2.) Event feuerst und dafür den Progress-Bar um 10 
(5, 2) Schritte weiter stellst? Dadurch werden wesentlich weniger 
Updates der GUI erforderlich und Du dürftest synchron bleiben.

Gruß
Markus

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

gute idee eigentlich, wieso komm ich nie auf sowas... ;)

von Markus V. (valvestino)


Lesenswert?

Peterle Anonym schrieb:
> ein thread der ständig ein reveive bit pollt. wurde was empfangen, wird
> ein event getriggert:

Das ist mir auch noch aufgefallen: Was heißt "der ständig ein receive 
bit pollt"? Wird da auf ein Event gewartet oder ist das wirklich ein 
aktives Polling? Soll heißen, der Thread verbrät laufend CPU-Zeit damit, 
abzufragen ob ein Ereignis anliegt? Wenn letzteres der Fall ist, geht 
hier mit ziemlicher Sicherheit die von der GUI benötigte Rechenzeit 
flöten.

Gruß
Markus

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

ja gut, er wird ja schon mit nem thread.sleep zyklisch schlafen gelegt, 
aber wenn ich mir das so anschaue, sind 25 ms schon recht wenig oder? 
wobei ich noch länger warten könnte, da der FTDI treiber einen software 
puffer hat...

von Markus V. (valvestino)


Lesenswert?

25ms Wartezeit sind eigentlich schon recht viel. Ich denke nicht, das 
das Problem daher rührt. Aber versuche doch mal 100 ms oder sogar 250 ms 
zu warten und schaue, ob sich was ändert. Ansonsten einfach meinen 
ersten Vorschlag: Reduziere die Anzahl der Updates.

Gruß
Markus

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

alles klar, werde nach feierabend mal testen. danke.

achjs, wird nicht eh durch den scheduler der clr jede 10 ms der nächste 
thread in der queue abgearbeitet? das wäre doch dann auch genug zeit?!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.