2017-08-21 6 views
2

Ich arbeite mit einem WinForm, von dem alle Prozesse gesteuert werden, die ich brauche. Jetzt versuche ich eine BackgroundWorker mit einer ProgressBar und eine Abbrechen-Schaltfläche in meinen Code zu integrieren. Ich möchte, dass es lokal um meinen Code geht und nicht in einer separaten Methode. Um dies zu testen, wird ein neues Formular mit einer Fortschrittsleiste (noch nicht aktiv) und einer Schaltfläche zum Stoppen einer for-Schleife erstellt. Der Code funktioniert jedoch nicht (und der Fortschrittsbalken ist noch nicht einmal enthalten). Das Formular friert sofort ein (siehe Bild), daher kann ich den Abbrechen-Button nicht testen. Die for-Schleife wird jedoch ausgeführt und "Done: " + l.ToString() wird angezeigt. Wie kann ich das lösen?Formular einfrieren mit BackgroundWorker

void stopMeasurement(object sender, EventArgs e) 
{ 
    stopMeas = true;  
} 

public void testcancel() // Test method which is triggered manually 
{ 
    int l = 0; 

    MetingProgress metingProgress = new MetingProgress(); 
    metingProgress.btnCancelmeting.Click += new EventHandler(stopMeasurement); 

    BackgroundWorker worker = new BackgroundWorker(); 
    worker.WorkerSupportsCancellation = true; 
    worker.DoWork += (sender, args) => 
    {      
     for (int k = 0; k < 10; k++) 
     { 
      Thread.Sleep(1000); 
      l++; 

      if (worker.CancellationPending) 
       break; 
     } 

     MessageBox.Show("Done: " + l.ToString()); 

    }; 
    worker.RunWorkerAsync(); 

    while (worker.IsBusy) 
    { 
     if (stopMeas) 
      worker.CancelAsync(); 
    } 

    metingProgress.Dispose(); 
    MessageBox.Show("All done"); 

} 

enter image description here

+0

Ja, ich habe bereits meinen Code für die verschiedenen Aufgaben, die in den Hintergrundarbeiter gehen müssen, also würde ich gerne ein Setup wie in meinem Beispiel haben. Die for-Schleife ähnelt den Aufgaben, die ausgeführt werden sollen. – 10a

+0

ok Ich sehe, dann 'testcancel' ist eigentlich eine Methode, einfach den Thread auszulösen und es im Hintergrund laufen zu lassen, und das ist eigentlich nur zu tun.Da der Thread länger laufen wird, solltest du wirklich nicht versuchen das Formular bei dieser Methode zu entfernen! es wäre die Aufgabe der Thread-Methode. auch die 'MessageBox.Show (" All done ");' -Zeile gehört in das 'DoWork'-Ereignis, da eigentlich nur der Thread selbst weiß, wann der Job erledigt ist, nicht die Methode, die den Thread –

Antwort

4

Die Form friert sofort

ist dies, weil Sie eine while-Schleife noch auf dem Haupt-Thread ausgeführt wird! Das Formular reagiert also nicht. Dies wird Buisy-Warten genannt. Sie können die Methode CancelAsync nicht aufrufen.

Eine Lösung könnte sein, die Schleife zu entfernen und den Anruf in die Schaltfläche Ereigniscode Abbrechen platzieren:

void stopMeasurement(object sender, EventArgs e) 
{ 
    stopMeas = true; 
    worker.CancelAsync(); 

} 

Was haben Sie im Grunde getan ist: Sie ein zweites Abbruch-Token erstellt. So könnte eine weitere Möglichkeit zu benutzen, nur stopMeas den Hintergrund Vorgang abzubrechen:

worker.DoWork += (sender, args) => 
{      
    for (int k = 0; k < 10; k++) 
    { 
     Thread.Sleep(1000); 
     l++; 

     if (stopMeas) 
      break; 
    } 

    string mes = stopMeas ? "Done: " + l.ToString() : "Task aborted!"; 
    MessageBox.Show(mes); 

}; 

EDIT: auch diese Zeile:

metingProgress.Dispose(); 

könnte zu einer ObjectDisposed Ausnahme führen. Wenn der Hintergrundprozess noch läuft und Sie versuchen, Ihre Statusleiste zu aktualisieren, haben Sie das Formular bereits freigegeben. Sie sollten diese Zeile entfernen und sie dem Garbage Collector überlassen.

+0

Die Schleife entfernen? Aber dann ist nichts mehr im Arbeiter? – 10a

+0

Er spricht über die 'while (worker.IsBusy)' Schleife. – Fildor

+0

Ja, ich habe es gerade gesehen und es funktioniert jetzt. – 10a

4

Dieser Code ist Ihr Problem:

while (worker.IsBusy) 
{ 
    if (stopMeas) 
     worker.CancelAsync(); 
} 

Ihre GUI-Thread ist in dieser Schleife, bis der Arbeitnehmer erfolgt. Sie müssen Ihre Worker-Instanz vom EventHandler aus erreichbar machen und von dort workers.CancelAsync() aufrufen.


Außerhalb dieser, würde ich persönlich den Code in zwei Schritten verbessern:

  1. die ganze Background in die MetingProgress Klasse verschieben

    und seinem Konstruktor einen Delegaten für die eigentliche Arbeit Umsetzung nehmen machen.

  2. Verwenden Sie TAP (Task async Pattern), d. H. Async/await Task with Progress und CancellationToken.

+0

Ich versuche zu expandieren mein Code Schritt für Schritt und zu verstehen, wie es genau funktioniert, so werde ich wahrscheinlich mit etwas enden, was Sie vorschlagen. – 10a

+2

Der Übergang zu TAP braucht wirklich etwas Zeit, um sich einzuhüllen. Nehmen Sie sich Zeit, aber es ist definitiv einen Versuch wert. Schritt für Schritt ist es das Beste, was du lernen kannst. Viel Glück! – Fildor