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 所做的那样)并且整个执行将在一个线程上发生。
更新了答案: 等待多个不同任务完成的真正方法需要异步等待而不是后台工作程序。
#我知道有很多关于 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 所做的那样)并且整个执行将在一个线程上发生。