C#BackgroundWorker - 我应该如何摆脱DoEvents

时间:2010-10-28 21:19:56

标签: c# backgroundworker doevents

我正在试图找出处理单击按钮点击触发的后台工作人员的最佳方法。我用3个单选按钮和一个标签创建了一个非常简单的表单。每个单选按钮共享相同的事件radioButton_CheckedChanged。如果事件完成,那么我将标签更新为“完成”。如果在事件完成之前单击另一个单选按钮,则将标签更新为已取消。下面是我在这个快速示例中编写的代码。尽管应用程序倾向于按预期运行,但我关注的是使用Application.DoEvents。我有什么替代方案。出于显而易见的原因,我在IsBusy时无法入睡。我是否认为这一切都错了,或者有更好的方法吗? 谢谢,poco

private void radioButton_CheckedChanged(object sender, EventArgs e)
{
  RadioButton rb = sender as RadioButton;
            if (rb.Checked)
            {
                if (backgroundWorker1.IsBusy)
                {
                    backgroundWorker1.CancelAsync();
                    while (backgroundWorker1.IsBusy)
                        Application.DoEvents();
                }

                backgroundWorker1.RunWorkerAsync();
            }
        }

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            for (int i = 0; i < 100 && !worker.CancellationPending; ++i)
                Thread.Sleep(1);

            if (worker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
                label1.Text = "Canceled";
            else
                label1.Text = "Complete";
        }

2 个答案:

答案 0 :(得分:7)

您应该将BackgroundWorker完成时必须运行的代码移动到RunWorkerCompleted hander中。在伪代码中:

private void radioButton_CheckedChanged(object sender, EventArgs e)
{
    // ...

    if (backgroundWorker1.IsBusy)
    {
        backgroundWorker1.CancelAsync();
        addJobToQueue();   // Don't wait here, just store what needs to be executed.
    } else {
        backgroundWorker1.RunWorkerAsync();
    } 
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled) {
        label1.Text = "Canceled";
    }
    else {
        label1.Text = "Complete";
    }

    // We've finished! See if there is more to do...
    if (thereIsAnotherJobInTheQueue())
    {
         startAnotherBackgroundWorkerTask();
    }
}

答案 1 :(得分:0)

DoEvents不应该如此随便。有更好的方法。其中一个非常好的是described here in SO。这个答案可能最适合你。

因此,您的解决方案变为:

private AutoResetEvent _resetEvent = new AutoResetEvent(false);

private void radioButton_CheckedChanged(object sender, EventArgs e)
{
    RadioButton rb = sender as RadioButton;
    if (rb.Checked)
    {
        if (backgroundWorker1.IsBusy)
        {
            backgroundWorker1.CancelAsync();
            _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
        }

        backgroundWorker1.RunWorkerAsync();
    }
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    for (int i = 0; i < 100 && !worker.CancellationPending; ++i)
        Thread.Sleep(1);

    if (worker.CancellationPending)
    {
        e.Cancel = true;
    }
    _resetEvent.Set();
}