Laden...

Toolstrip Progressbar? Fängt zu spät beim Aktualisieren an

Erstellt von MrFluffy vor 12 Jahren Letzter Beitrag vor 12 Jahren 3.605 Views
M
MrFluffy Themenstarter:in
32 Beiträge seit 2011
vor 12 Jahren
Toolstrip Progressbar? Fängt zu spät beim Aktualisieren an

Tach,

ich spare mir mal den Code, da das ganze eher ein Problem logischer Natur ist.

Zur Situation:

  • Es findet in einem gesonderten Thread eine Socket Kommunikation statt die einer kopfgesteuerten Schleife alle Bytes abarbeitet und entsprechend aufbereitet.
  • Während dieses Prozesses wird stets eine Methode im Controller angetriggert
  • Die Methode im Controller wiederrum triggert die View an und verursacht ein progressbar.value+=10

Die Probleme sind Folgende:

  • Die Progressbar gelangt nicht zum Ende, d. h. am Ende sind keine 100% erreicht. Zur Laufzeit ist nicht bekannt wieviel Bytes tatsächlich ankommen werden, daher fällt eine Berechnung einer geeigneten Schrittweite entsprechend schwer aus.

  • Die Progressbar verhält sich synchron, d.h. erst wenn der Prozess am Ende ist wird ganz schnell der Balken gefüllt. Ich möchte aber eine Progressbar anzeigen um zu signalisieren es passiert etwas, daher ist dieses Control im Moment ziemlich sinnfrei

  • Ob ich BeginInvoke oder Invoke verwende spielt hierbei keine Rolle

Hinweis: Es handelt sich um eine statusstrip Progressbar

Wäre super wenn mir Jemand einen Tipp geben könnte

1.552 Beiträge seit 2010
vor 12 Jahren

Hallo MrFluffy,

das Problem ist immer dasselbe:
[FAQ] Warum blockiert mein GUI?

Die Progressbar gelangt nicht zum Ende, d. h. am Ende sind keine 100% erreicht

Bist du dir sicher dass der letzte Wert, den du setzt 100 ist?
Das ist so ein Problem wennman
99.999.999/100.000.000 * 100 = 99,999999
dividiert. Da Value ein int ist wird 99 als Wert übernommen und nicht der aufgerundete 100er.

Gruß
Michael

Mein Blog
Meine WPF-Druckbibliothek: auf Wordpress, myCSharp

G
538 Beiträge seit 2008
vor 12 Jahren

Hallo MrFluffy,

wenn man nicht weiß und auch nicht ermitteln kann, wann man am Ende sein wird, so nutzt man häufig keine Progressbar mit Steps sondern mit Marquee - also eine laufende ohne genaue Daten (Windows 7 macht das zum Beispiel wenn es noch nicht weiß, wieviel es kopieren wird).

Du könntest dann zusätzlich in der Statusleiste einfach anzeigen, wieviel Daten bereits verarbeitet wurden.

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

M
MrFluffy Themenstarter:in
32 Beiträge seit 2011
vor 12 Jahren

das Problem ist immer dasselbe:

>

Meine GUI blockiert nicht, da ich diese völlig frei verschieben, minimieren und Strichmännchen drin malen kann. Die Progressbar wird zu spät aktualisiert weil etwas synchron abläuft was nicht synchron laufen sollte.

Obwohl der Controller mehrmals während des Prozesses angetriggert wird, wird erst am Ende des Threads die Progressbar in schnellen Intervallen gefüllt.

Bist du dir sicher dass der letzte Wert, den du setzt 100 ist?

Der Wert unterliegt der willkür weil ->

Zur Laufzeit ist nicht bekannt wieviel Bytes tatsächlich ankommen werden, daher fällt eine Berechnung einer geeigneten Schrittweite entsprechend schwer aus.

Die Konsequenz ist das ich keine Werte habe um irgendetwas zu berechnen, denn die Schleife läuft solange Bytes != 0 sind.

wenn man nicht weiß und auch nicht ermitteln kann, wann man am Ende sein wird, so nutzt man häufig keine Progressbar mit Steps sondern mit Marquee - also eine laufende ohne genaue Daten (Windows 7 macht das zum Beispiel wenn es noch nicht weiß, wieviel es kopieren wird).

Prickelnd finde ich das auch nicht wirklich aber ist die einzige Möglichkeit wie ich mit wechselseitiger Laufzeit klar komme.

Du könntest dann zusätzlich in der Statusleiste einfach anzeigen, wieviel Daten bereits verarbeitet wurden.

Die Problematik das der Thread nicht in Echtzeit die Gui aktualisiert wird aber auch so nicht gelöst.

Der WorkerThread -> ruft je Iteration eine Controller Methode auf -> Controller ruft aktualisierungsmethode für die Progressbar auf (Methode invoked bei Bedarf) und setzt den Value hoch.

Also ich vermute das meine Änderungswünsche in eine Schlange eingereiht werden, welche erst am Threadende verarbeitet wird.

D. h. ich muss bei C# etwas mit Gui Repaint o. Ä. suchen 🤔

5.742 Beiträge seit 2007
vor 12 Jahren

Also ich vermute das meine Änderungswünsche in eine Schlange eingereiht werden, welche erst am Threadende verarbeitet wird.

"Vermuten" ist immer so eine Sache 😉

Evtl. überlastest du den UI-Thread ja durch zu viele Aktualisierungsrequests - nimm mal ein Timer (aber den aus System.Windows.Forms - der braucht kein Invoke), der ein volatile Feld alle 250ms ausliest und entsprechend den Fortschritt setzt.
Dieses Feld kannst du dann aus deinem Arbeitsthread einfach beschreiben.

Evtl. kannst du ja mal ein Minimalbeispiel posten, das dieses Verhalten zeigt.

M
MrFluffy Themenstarter:in
32 Beiträge seit 2011
vor 12 Jahren

Evtl. überlastest du den UI-Thread ja durch zu viele Aktualisierungsrequests - nimm mal ein Timer (aber den aus System.Windows.Forms - der braucht kein Invoke), der ein volatile Feld alle 250ms ausliest und entsprechend den Fortschritt setzt.
Dieses Feld kannst du dann aus deinem Arbeitsthread einfach beschreiben.

Evtl. kannst du ja mal ein Minimalbeispiel posten, das dieses Verhalten zeigt.

Das sind nur 4-5 Iterationen....
Aber klar deinen Vorschlag teste ich gerne. Wobei mir im Moment nur gerade das Verständnis wichtiger ist. Wieso sich etwas so verhält wie es sich verhält.

BTW: Etwas befremdlich ist bei C# auch das man eine Methode und keine Objekte dem Thread übergibt. Zumindest ist es eine Besonderheit.

Ein Minimalbeispiel sieht so aus:

class socketproto


public void makeRequest(string method)
        {
            if (method.Equals("wuff"))
            {
                connThread = new Thread(new ThreadStart(postRequest));
                connThread.Start();

            }
        }

public void postRequest()
        {                       
            //Etliche Stringoperationen mit StringBuffer    
        
            result = socketSendReceive(domain, port, request.ToString());

            //Ergebnis ins Model legen
            this.controller.getPostData(result);
        }

private string socketSendReceive(string server, int port, String request)
        { 
            // Empfange die Daten und konvertiere sie
            do {
                bytes = sock.Receive(bytesReceived, bytesReceived.Length, SocketFlags.None);
                //Progressbar aktualisieren
                this.controller.makeProgress();
            } while (bytes > 0);
            return "bla";
        }
    }

class controller


public void makeProgress()
        {
            ((BlaForm)this.form).makeProgress();
        }

class BlaForm


 public void makeProgress()
        {
            if (this.statusStrip1.InvokeRequired)
            {
                this.statusStrip1.Invoke(new MethodInvoker(increateProgress));
                return;
            }
            
            Console.WriteLine("bla");
            this.progressBar.Value += 20;
        }

Ach ja ich hab schon mit den Methoden update(), und refresh() versucht, hilft aber nix

Momentan habe ich noch recht konkrete Casts rein testweise, da muss man sich auch nicht wundern.

5.742 Beiträge seit 2007
vor 12 Jahren

Welchen Wert verwendest du im Moment als Maximum für die Progressbar?

BTW: Verwende lieber den ThreadPool, statt selber Threads zu erzeugen.

//EDIT: Also folgendes Programm zeigt einen wunderbar synchronen Fortschritt an - es muss wohl an deiner Wahl des Maximums liegens:


public partial class Form1 : Form
{
    private volatile int _progress;
    private Timer _timer;

    public Form1()
    {
        InitializeComponent();

        this._timer = new Timer();
        this._timer.Tick += timer_Tick;
        this._timer.Interval = 250;
        this._timer.Start(); //Sollte man auch besser nur während des Vorgangs laufen lassen
    }

    void timer_Tick(object sender, EventArgs e)
    {
        this.toolStripProgressBar1.Value = this._progress;
    }

    private void DoWork()
    {
        Random rand = new Random();

        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(rand.Next(50, 500));

            this._progress = i;
        }

        MessageBox.Show("Done!"); //Kein Best Practice, aber für die Demo tut's ;-)
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this._progress = 0;

        ThreadPool.QueueUserWorkItem(o => this.DoWork());
    }
}

M
MrFluffy Themenstarter:in
32 Beiträge seit 2011
vor 12 Jahren

Welchen Wert verwendest du im Moment als Maximum für die Progressbar?

Ich habe die Standardwerte, d.h. das Interval [0,100] ?

Kann mir nicht vorstellen was daran falsch sein könnte.

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo MrFluffy,

schau dir im Debugger (oder noch besser per Logging) den zeitlichen Ablauf an. Wichtig ist vor allem, wann die makeProgress-Methode aus welchem Thread (z.B. Thread.CurrentThread.Name) aufgerufen wird. Kommen schon die ursprünglichen Aufrufe verzögert oder sind erst die rekursiven Aufrufe verzögert.

Wenn ersteres liegt wohl gar kein GUI Problem vor und du musst dir Ursache bei den Sockets suchen. Wenn letzteres musst du dich nur strikt an [FAQ] Warum blockiert mein GUI? und [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) halten, um dein Problem zu lösen.

So oder so, sollte der Thread damit zu Ende sein.

herbivore

5.742 Beiträge seit 2007
vor 12 Jahren

Kann mir nicht vorstellen was daran falsch sein könnte.

Du sagtest ja, dass du nicht genau weißt, wie viele Bytes ankommen - und wenn eben nur so viele ankommen, dass der Value der Progressbar hinterher auf 80 (von maximum 100) steht, ist es klar, dass sie nicht voll ist.
Das meinte ich damit. 😉

M
MrFluffy Themenstarter:in
32 Beiträge seit 2011
vor 12 Jahren

schau dir im Debugger (oder noch besser per Logging) den zeitlichen Ablauf an. Wichtig ist vor allem, wann die makeProgress-Methode aus welchem Thread (z.B. Thread.CurrentThread.Name) aufgerufen wird. Kommen schon die ursprünglichen Aufrufe verzögert oder sind erst die rekursiven Aufrufe verzögert.

Guter Tipp, der bringt mich in die richtige Richtung

Wie erwartet wird die makeProgress Methode durch den SocketThread angesteuert.
Anhand der zeitlichen Reihenfolge konnte ich entnehmen, das vom SocketMethoden Aufruf bis zum Ende der ersten Iteration innerhalb der Methode...10 Sekunden vergehen. Die weiteren Aufrufe liegen im Millisekundenbereich da sich die Sekunden nicht ändern.

Demzufolge wird die Progressbar falsch angesteuert. D. h. man müsste am Empfangspuffer des Sockets ansetzen und während dieses Lesezykluses die Progressbar aktualisieren. Aber dort komme ich nicht heran, weshalb eine Progressbar Timer Lösung nur möglich ist.

Du sagtest ja, dass du nicht genau weißt, wie viele Bytes ankommen - und wenn eben nur so viele ankommen, dass der Value der Progressbar hinterher auf 80 (von maximum 100) steht, ist es klar, dass sie nicht voll ist.
Das meinte ich damit. 😉

Achso das meintest Du....

Das habe ich mit meiner Aussage bzgl. der Schwierigkeit der Bestimmung einer geeigneten Schrittweite im Grunde angedeutet, wohl aber nicht klar genug ausgesagt dafür müsste man mich besser kennen.

T
415 Beiträge seit 2007
vor 12 Jahren

Mal ein ganz anderer trivialer Ansatz: Wenn es dir nur darum geht, anzeigen zu wollen, dass dein Programm noch arbeitet, dann setze doch einfach deine Progressbar auf die Style-Eigenschaft auf ProgressBarStyle.Marquee und am Ende des Threads wieder auf ProgressBarStyle.Blocks. Dadurch hast du während des Vorgangs eine kontinuierlich laufende Progressbar, die am Ende wieder sauber anhält, wenn die Arbeit getan ist.

Hinweis von herbivore vor 12 Jahren

Den Vorschlag hat Grumbler85 schon weiter oben gemacht und MrFluffy hat auch schon darauf geantwortet.