【问题标题】:C# BackgroundWorker - how should i get rid of DoEventsC# BackgroundWorker - 我应该如何摆脱 DoEvents
【发布时间】:2010-10-28 21:19:56
【问题描述】:

我正在尝试找出处理因单选按钮点击而触发的后台工作人员的最佳方式。我创建了一个非常简单的表单,其中包含 3 个单选按钮和一个标签。每个单选按钮共享相同的事件 radioButton_CheckedChanged。如果事件完成,那么我将标签更新为“完成”。如果您在事件完成之前单击另一个单选按钮,则将标签更新为已取消。下面是我在这个快速示例中编写的代码。尽管应用程序往往按预期运行,但我担心的是 Application.DoEvents 的使用。我有什么替代方案。由于明显的原因,我在 IsBusy 时无法入睡。我是不是把这一切都错了,还是有更好的方法来做到这一点? 谢谢,波科

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";
        }

【问题讨论】:

    标签: c# backgroundworker doevents


    【解决方案1】:

    您应该将 BackgroundWorker 完成时必须运行的代码移到 RunWorkerCompleted 处理程序中。在伪代码中:

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

    【讨论】:

    • 好的,我按照这个,我去测试一下。我会假设 JobQueue 将在 GUI 线程上,所以我是否曾经冒过交叉线程的风险?我唯一会看到更改 jobQueue 是在 rb_checkchanged 和 bw_runco​​mpleted 中,它们应该回到主线程对吗?
    【解决方案2】:

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

    【讨论】:

    • 这导致->这个BackgroundWorker当前很忙,调用backgroundWorker1.RunWorkerAsync()时不能同时运行多个任务
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 1970-01-01
    • 2015-09-28
    • 1970-01-01
    相关资源
    最近更新 更多