终止多个后台辅助角色



我有一个主线程调用多个后台工作者(在 .net/c# 中(。 这些线程中的每一个都启动一个进程以运行可执行文件。 当一个进程结束时,我想告诉主线程杀死所有其他线程及其各自的进程。在它们全部停止后,我想知道这一点并继续运行主线程进行后处理。
我保留了这些外部进程的列表,因此我可以毫无问题地将它们全部杀死。我的问题是如何杀死所有这些背景工作者。我试图保留与它们关联的线程列表,并从终止的第一个线程中杀死它们,但显然这不会杀死后台工作线程本身,因为 runworkercomplete 方法仍然被多次调用。 有没有人知道如何以一种好的方式杀死这些工人?我应该以某种方式通知主线程杀死其他工人吗?

我建议使用 async/await 和 CancelTokenSources。我也会给出如何使用 BackgroundWorkers 的建议,但由于 async/await 更方便(而且更短(,所以它排在第一位。

async/await 很方便,因为它为您提供了所需的功能,而不会增加太多复杂性。

private async void SomeEvent(object sender, EventArgs e)
{
CancellationTokenSource cts = new CancellationTokenSource();
var waiter1 = DoSomething(cts.Token);
var waiter2 = DoSomethingElse(cts.Token);
// etc.
// Wait for the first one to finish, then cancel
await Task.WhenAny(waiter1, waiter2, ...).ConfigureAwait(false);
cts.Cancel();
// wait for the remainder to finish
await Task.WhenAll(waiter1, waiter2, ...).ConfigureAwait(false);
// Do Postprocessing
}

你的"服务员"看起来像这样:

private async Task DoSomething(CancellationToken token)
{
// Do stuff
// Periodically check if someone has finished
if (Token.IsCancellationRequested)
{
// clean up
return;
}
}

async/await 代码有一些陷阱,包括死锁。由于这听起来像是一个快速的项目(我可能是错的(,它似乎是一个学习的好地方 - 特别是如果没有大量的代码库可以返工。如果你想了解更多,我认为Stephen Cleary的博客是一个很好的起点,尤其是他的介绍。

另一方面,如果您绝对确定要使用 BackgroundWorkers...好吧,我不怪你,但我也不羡慕你。

首先,你的工人必须知道其他人是否先完成。使用已完成的 BackgroundWorker 的 RunWorkerDone 方法取消其他 BackgroundWorkers:

private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{ 
// Check for errors...
}
else if (e.Cancelled)
{
// Mark that this one has finished
}
else
{
// Assuming you have a set of BackgroundWorkers called "workers"
foreach (var bgw in workers)
bgw.CancelAsync();
// other stuff...
}
}

然后,在 DoWork 方法的末尾添加一些代码以报告取消...

private void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Do stuff...
// When "RunWorkerCompleted" is called, let it know whether this worker has been cancelled.
e.Cancel = worker.CancellationPending;        
}

仅此而已。您还可以检查工作人员。取消定期待处理,看看是否可以提前完成,但不要忘记分配工作人员。取消待定 e.在您返回之前取消!

最后一件事:如果您希望在所有工作人员完成(并且只有那时(继续后处理,您需要一种方法来标记特定工作人员何时完成(取消其他工作人员(,然后找出他们何时完成(以便您可以开始后处理(。这是可行的,而且不太困难 - 在我的头顶上,我会用一个Dictionary<BackgroundWorker, bool>来指示哪些工人已经完成。不过,这是您可以使用async/await避免的另一块混乱。

从这个答案来看,似乎没有办法杀死背景工作者。但是,此答案显示了通过覆盖OnDoWork并保持对Thread.CurrentThread的引用来解决此问题的方法。不过,我仍然会尝试让这些后台工作人员检查取消通知。

最新更新