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.
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."
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.
die updates für den progessbar landen in der nachrichten warteschlange, du kannst das ab arbeiten der message-pump mit Application.DoEvents() veranlassen.
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 | }
|
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
__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.
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!!!
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
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
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...
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.