2016-03-22 10 views
0

Es ist BackgroundWorker in DoWork Kunstsammlung zu erstellen und ParallelForeach Verarbeitung.Warum hört mein BackgroundWorker auf?

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    int cnt = (int) e.Argument; 

    List<int[]> ListArrays = new List<int[]>(); 
    for (int i=0; i<cnt ; i++) 
    { 
     Random rnd = new Random((int)DateTime.Now.Ticks); 
     int length = rnd.Next(5000, 100000); 

     ListArrays.Add(new int[length]); 

     for (int j = 0; j < ListArrays[i].Length; j++) 
     { 
      ListArrays[i][j] = rnd.Next(0, 1000000); 
     } 
    } 

    int progress=0; 

    Parallel.ForEach(ListArrays, item => 
     { 

      if (_backgroundWorker != null) 
      { 
       if (_backgroundWorker.WorkerReportsProgress) 
       { 
        _backgroundWorker.ReportProgress((int)(progress/(cnt/100M))); 

       } 
       if (_stopThread) 
       { 
        _busy.WaitOne(); 
       } 
       if (_backgroundWorker.CancellationPending) 
       { 

        e.Cancel = true; 
        return; 
       } 

       Worker worker = new Worker(); 
       worker.FindPrimes(item); 
       CounterPrimes += worker.CounterPrimes; 
       progress++; 
      } 
     }); 
} 

Es gibt Schaltflächen "Stop", "Pause" und "Fortsetzen". Gleich nach dem Start funktioniert der Stop richtig. Aber wenn Sie eine "Pause" machen, dann fahren Sie fort, nachdem eine Stopp-Taste nicht funktioniert, warum?

AutoResetEvent _busy = new AutoResetEvent(false); 
bool _stopThread; 
private void StopBtn_OnClick(object sender, RoutedEventArgs e) 
{ 
    // _stopThread = false; 
    //_busy.Set(); 
    _backgroundWorker.CancelAsync(); 
} 

private void PauseBtn_OnClick(object sender, RoutedEventArgs e) 
{ 
    PauseBtn.IsEnabled = false; 
    _stopThread = true; 
    ResumeBtn.IsEnabled = true; 
} 

private void ResumeBtn_OnClick(object sender, RoutedEventArgs e) 
{ 
    ResumeBtn.IsEnabled = false; 
    PauseBtn.IsEnabled = true; 
    _stopThread = false; 
    _busy.Set(); 
} 

Russuan Question.

+0

_But wenn Sie eine Pause machen, dann weiterfahren, nachdem eine Stopp-Taste nicht funktioniert, warum? _ Sie sagten es selbst. STOP bedeutet STOP, danach gibt es keine Fortsetzung. Sie müssen es anhalten, um fortfahren zu können? – Mafii

+0

@Mafii Frage bearbeiten, tut mir leid –

+1

Ich bin überrascht, es gibt 'ru.stackoverflow'. Ist es für * Elite * (lol) Gruppe von russischen Programmierern? P.S .: Ich spreche Russisch, werde aber nie dorthin gehen, "ru.stackoverflow" ist wie "idontspeekenglishkkthxbai.stackoverflow". Englisch ist nützlich .. ich denke. – Sinatr

Antwort

1

Wenn Sie _backgroundWorker.CancelAsync() anrufen, markiert es die CancellationPending Flagge als wahr, aber es liegt in Ihrem Verantwortungsbereich, regelmäßig zu überprüfen, ob CancellationPending markiert wurde und nicht mehr läuft. In Ihrem Code überprüfen Sie CancellationPending, aber Sie überprüfen es nur einmal, ganz am Anfang. Der Großteil der Arbeit wird in der Worker Klasse ausgeführt, aber es wird nur nach Ihrer Prüfung durchgeführt, deshalb wird es nur am Anfang funktionieren (unabhängig davon, ob Sie auf Pause geklickt haben oder nicht). Aus dem gleichen Grund wird Ihre Pause-Funktion auch nur ganz am Anfang funktionieren, da Sie nur Ihr _stopThread Flag einmal überprüfen.

Als Beispiel Lösung, können Sie den Code verschieben, der für die Pause und dem Anschlag auf eine separate Methode überprüft:

private bool shouldStopWork(DoWorkEventArgs e) 
{ 
    _busy.WaitOne();   
    if (_backgroundWorker.CancellationPending) 
    { 
     e.Cancel = true; 
     return true; 
    } 
    return false; 
} 

Sie eine Func erstellen können hierfür die Veranstaltung args kapseln: Func<bool> shouldStop =() => shouldStopWork(e); und dann übergeben Sie diese Func an Ihre Worker Klasse in seinem Konstruktor, dann lassen Sie die Worker Klasse es regelmäßig in seiner FindPrimes Methode aufrufen und beenden Sie die Methode, falls erforderlich.

Ein anderes Problem, das Sie haben, ist, dass Sie AutoResetEvent verwenden, was bedeutet, dass, wenn Sie angehalten und dann fortgesetzt, nur einer der Arbeiter aufwacht, was nicht das ist, was Sie wollen. Sie möchten, dass alle Arbeiter aufwachen, also müssen Sie stattdessen ManualResetEvent verwenden. Beachten Sie auch, dass ich die Verwendung für die _stopThread Flagge entfernt, es ist nicht notwendig, da es bereits ein Flag innerhalb des Reset-Ereignis, wenn Sie müssen das Ereignis auf Ihrer Pause geklickt Ereignis zurück: _busy.Reset();

Letzter Punkt: wie @Panagiotis Kanavos erwähnt, BackgroundWorker ist die alte Art der Dinge zu tun, können Sie async/erwarten, Aufgaben und die Progress Klasse, um den Fortschritt zu melden (ein Beispiel: http://simplygenius.net/Article/AncillaryAsyncProgress), aber beachten Sie, dass dies Sie nicht aus der Verantwortung der regelmäßigen Überprüfung der Notwendigkeit befreit in Ihrer Worker Klasse zu stornieren. Der Grund dafür ist, dass die einzige Möglichkeit, den Löschvorgang ohne die Hilfe der "anderen Seite" durchzuführen, der gewaltsame Abbruch des Threads ist, der jedoch die Anwendung in einem unbestimmten Zustand verlassen kann. Aus diesem Grund wurde Thread.Abort ebenfalls eingestellt.

+0

Erwägen Sie, die beiden Teile Ihrer Antwort zu tauschen. Das Bit, das "Auto/ManualResetEvent" verwendet, ist entscheidend für die Behebung des Problems, während der Großteil der vorhergehenden Antwort (über die Unterstützung der kooperativen Löschung in 'FindPrimes') eher eine Empfehlung ist und je nachdem, wie lange es sinnvoll ist jeder 'FindPrimes' Aufruf dauert. –

Verwandte Themen