更新了答案: 等待完成许多不同任务的真正方法需要异步等待而不是后台工作者。
#我知道有很多关于背景工作者的讨论,但我一直在寻找,但找不到答案。
这是我的代码示例(基本逻辑,实际代码要长得多),我想知道是否有办法解决这个问题:
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
});
}
答案 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那样)并且整个执行将在一个线程上发生。