RunWorkerAsync 在完成时关闭 BackgroundWorker
本文关键字:BackgroundWorker 完成时 RunWorkerAsync | 更新日期: 2023-09-27 18:34:22
我有一个后台工作者,需要根据选中的复选框数量多次调用 - 我写这个是为了获取复选框值并将它们放入List
中。
List repSelected = new List();
这是填充的,然后像这样迭代:
foreach (string rep in repSelected)
{
backgroundWorker1.RunWorkerAsync(rep);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
异步的DoWork代码如下所示:
BackgroundWorker worker = sender as BackgroundWorker;
string rep = e.Argument.ToString();
if (worker.CancellationPending == true)
{
e.Cancel = true;
}
else
{
DirectoryExists(rep);
ProcessRunner(rep); //Rars some large files - expensive
}
然后,该进程运行 WorkerComplete,问题是当进程返回执行 Worker 的下一次迭代时,它崩溃了,表示工作线程很忙 - 即使工作线程已返回其 WorkerCompleted 状态。
如何确保线程在循环的下一次迭代之前关闭?
注意:我对包含!backgroundWorker1.IsBusy()
的后台工作者有一个条件,但这(显然)只是跳过了剩余的迭代而没有执行。
如果要按顺序处理每个项目,则没有理由为每个任务使用单独的后台工作者。因此,最好将foreach循环移动到DoWork方法中。但是,如果要处理 paralell 中的所有项目,则需要为每个项目创建一个后台工作线程。
您的 foreach 代码将立即触发所有元素的工作。这就是您获得异常的原因。
如果要按顺序启动工作线程,则只能在启动时调用一次 RunWorkerAsync,然后为每个 WorkerComplete-事件调用一次。但是,为什么不在工作代码中对每个进行处理呢?
后台工作者真的很忙,因为当第一次调用backgroundWorker1.RunWorkerAsync(rep);
时,它不会等待任何东西,然后是第二个,第三个,...立即调用调用。
您应该在每次调用时创建 BackgroundWorker ,然后就可以了。
来自 MSDN:
如果后台操作已在运行,则再次调用 RunWorkerAsync 将引发 InvalidOperationException。
因此,您不能使用BackgroundWorker
来维护任务队列(并且您可以按顺序推送所有任务,而无需等待前面的任务完成)。您对此有不同的解决方案,例如,如果您想继续使用BackgroundWorker
您可以这样做:
backgroundWorker1.RunWorkerAsync(repSelected);
然后更改您的DoWork
方法,如下所示:
BackgroundWorker worker = sender as BackgroundWorker;
foreach (string rep in (IEnumerable<string>)e.Argument)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
return;
}
else
{
DirectoryExists(rep);
ProcessRunner(rep); //Rars some large files - expensive
}
}
作为替代方法,您可以考虑更改执行此任务的方式,例如使用 System.Threading.Tasks.Task
或 ThreadPool
(直接或间接地,大多数并行操作将排队到池中)。
您可以使用 parallel.foreach 和多个背景工作者;
Parallel.ForEach(YourListofStrings,
(q) =>
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync(q);
});