为什么我的BackgroundWorker会停止?

时间:2016-03-22 11:20:23

标签: c# .net wpf

BackgroundWorker艺术品收藏中有DoWork来制作和ParallelForeach处理。

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++;
            }
        });
}

有按钮&#34;停止&#34;,&#34;暂停&#34;和&#34;恢复&#34;。在启动停止后正常工作。但如果你采取&#34;暂停&#34;然后在停止按钮不起作用后继续,为什么?

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

1 个答案:

答案 0 :(得分:1)

当您致电_backgroundWorker.CancelAsync()时,它会将CancellationPending标记为true,但您的代码有责任定期检查CancellationPending是否已标记并停止运行。

在您的代码中,您会检查CancellationPending,但您最初只会检查一次。大部分工作都是在Worker课程中完成的,但它只在你检查后执行,这就是为什么它只能在一开始就工作(无论你是否点击了暂停)。 出于同样的原因,您的暂停功能也只能在最开始工作,因为您只检查一次_stopThread标志。

作为示例解决方案,您可以将检查暂停和停止的代码移动到单独的方法:

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

您可以为此创建Func以封装事件参数Func<bool> shouldStop = () => shouldStopWork(e);,然后将此Func传递给其构造函数中的Worker类,然后使用Worker类在其FindPrimes方法中定期调用它,并在需要时退出该方法。

你遇到的另一个问题是你正在使用AutoResetEvent这意味着如果你暂停然后恢复,只有一个工作人员会醒来,这不是你想要的。您希望所有工作人员都醒着,因此您需要使用ManualResetEvent。另请注意,我删除了_stopThread标志的使用,因为重置事件中已经有一个标记,所以不需要它,尽管您需要在暂停点击的事件中重置事件:_busy.Reset();

最后一点:就像@Panagiotis Kanavos提到的那样,BackgroundWorker是旧的做事方式,你可以使用async / await,tasks和Progress类来报告进度(例如:{{ 3}}),但请注意,这并不能免除您定期检查Worker课程中需要取消的责任。这样做的原因是,在没有“另一方”帮助的情况下实现取消的唯一方法是猛烈地杀死线程,但这可能使应用程序处于未确定状态。这就是为什么Thread.Abort也被弃用的原因。