C#backgroundworker RunworkerCompleted vs async await

时间:2017-04-28 21:16:08

标签: c# asynchronous async-await backgroundworker

更新了答案: 等待完成许多不同任务的真正方法需要异步等待而不是后台工作者。

我知道有很多关于背景工作者的讨论,但我一直在寻找,但找不到答案。

这是我的代码示例(基本逻辑,实际代码要长得多),我想知道是否有办法解决这个问题:

 BackgroundWorker MCIATS1Worker = new BackgroundWorker();
    private AutoResetEvent _MCIATS1WorkerResetEvent = new AutoResetEvent(false);

    public MainWindow()
    {
        InitializeComponent();

        MCIATS1Worker = new BackgroundWorker();
        MCIATS1Worker.DoWork += new DoWorkEventHandler(MCIATS1Worker_DoWork);
        MCIATS1Worker.WorkerReportsProgress = true;
        MCIATS1Worker.WorkerSupportsCancellation = true;
        MCIATS1Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(MCIATS1_RunWorkerCompleted);

        for (int i = 1; i <= 10; i++)
        {
            //some code
            MCIATS1Worker.RunWorkerAsync();
            _MCIATS1WorkerResetEvent.WaitOne();
        }

    }

DoWork和runworkercompleted

void MCIATS1Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        //do something here
    }

    void MCIATS1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("hello world");
        _MCIATS1WorkerResetEvent.Set();
    }

由于某些原因,在循环结束之前不会触发MCIATS1_RunWorkerCompleted。显然,WaitOne正在保持循环 这是我的问题,

当工作人员实际完成工作时,为什么RunWorkerCompleted不会触发RunWorkerCompleted?

谢谢。

###更新的解决方案

这是正确的做法。

private async void WhateverFunction()
   {
    await Task.WhenAll(MCIATS1WorkerDoWorkAsync(param),...other tasks);
    }


private Task MCIATS1WorkerDoWorkAsync(bkgWorkParameter param)
    {
        return Task.Run(() =>
        {
            //Do whatever
        });
    }

2 个答案:

答案 0 :(得分:3)

之所以会发生这种情况,是因为当您使用BackgroundWorker时,它的RunWorkerCompleted事件会发布到调用SynchronizationContext的帖子的RunWorkerAsync

因为您在UI线程上调用RunWorkerAsync,所以在UI线程开始处理消息循环中的新消息之前,事件无法运行。但是,您阻止了UI线程通过_MCIATS1WorkerResetEvent.WaitOne();调用返回到消息循环。

所以归结为_MCIATS1WorkerResetEvent.Set();等待MCIATS1_RunWorkerCompleted触发停止阻止,MCIATS1_RunWorkerCompleted等待_MCIATS1WorkerResetEvent.Set();停止阻止UI线程,所以它是要处理的消息。

在完成任务之前,两件事都在等待另一件事完成,你有一个经典的死锁。

不需要for循环就可以发生这个问题,同样的问题会在循环或不循环的情况下发生,实际上循环永远不会运行它的第二次迭代,因为它将会死锁第一次通过,所以根本没有循环。

答案 1 :(得分:1)

根据您的MCIATS1Worker_DoWork方法所做的工作,您可以考虑使用async-await方法,这会使代码更加清晰。

private async Task MCIATS1WorkerDoWorkAsync()
{        
    await Task.Delay(1000) // do something asynchronously for 1 second
}

private async void MainWindow_Load(object sender, EventArgs e)
{
     for (int i = 1; i <= 10; i++)
     {
        //some code
        await MCIATS1WorkerDoWorkAsync();
        MessageBox.Show("hello world");
     }       
}

消息框每1秒显示10次。只有在await方法成功完成后,MCIATS1WorkerDoWorkAsync关键字才会继续循环。

使用async-await,您的表单将保持响应状态,如果DoWork方法执行某些IO操作,那么您将不会启动另一个线程(如BackgroundWorker那样)并且整个执行将在一个线程上发生。