C# backgroundworker RunworkerCompleted 与异步等待

C# backgroundworker RunworkerCompleted vs 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 和 runworker 已完成

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不会在worker实际完成工作时触发RunWorkerCompleted?

谢谢。

###更新的解决方案

这是正确的做法。

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


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

发生这种情况是因为当您使用 BackgroundWorker 时,它的 RunWorkerCompleted 事件被发布到调用 RunWorkerAsync.

的线程的 SynchronizationContext

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

所以归结起来就是 _MCIATS1WorkerResetEvent.Set(); 正在等待 MCIATS1_RunWorkerCompleted 触发以停止阻塞,而 MCIATS1_RunWorkerCompleted 正在等待 _MCIATS1WorkerResetEvent.Set(); 停止阻塞 UI 线程所以它是要处理的消息。

两件事情都在等待对方完成,然后自己完成,这就是典型的死锁。

这个问题不需要 for 循环,无论有没有循环都会发生同样的问题,事实上循环永远不会到达 运行 这是第二次迭代因为它会在第一次通过时发生死锁,所以根本没有循环。

根据你的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次。只有在 MCIATS1WorkerDoWorkAsync 方法成功完成后,await 关键字才会继续循环。

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