终止多个背景工作者

时间:2017-08-22 21:26:11

标签: c# multithreading backgroundworker

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

2 个答案:

答案 0 :(得分:2)

我建议使用async / await和CancellationTokenSources。我将提供有关如何使用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代码有一些问题,including deadlock。因为这听起来像一个快速的项目(我可能是错的),它似乎是一个学习的好地方 - 特别是如果没有大规模的代码库可以返工。如果你想了解更多,我认为Stephen Cleary的博客是一个很好的起点,特别是他的intro

另一方面,如果你完全确定你想要使用BackgroundWorkers ......那么我不怪你,但我也不羡慕你。

首先,你的工人必须先知道别人是否先完成了。使用完成的BackgroundWorker的RunWorkerCompleted方法取消其他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;        
}

就是这样。您还可以定期检查worker.CancellationPending以查看您是否可以提前完成,但不要忘记在返回之前将worker.CancellationPending分配给e.Cancel!

最后一件事:如果你希望在所有工人完成后(并且只有那时)继续后处理,你需要有办法标记特定工人何时完成(取消其他工人),然后一种方式找出他们什么时候完成(所以你可以开始后期处理)。这是可行的,而且不是太困难 - 从头顶开始,我会用Dictionary<BackgroundWorker, bool>来表明哪些工人已经完成了。不过,这是你可以通过异步/等待避免的另一个混乱。

答案 1 :(得分:0)

this answer开始,似乎没有办法杀死背景工作者。但是,this answer通过覆盖OnDoWork并保留对Thread.CurrentThread的引用来显示解决方法。不过,我仍然会尝试让那些Backgroundworkers检查取消通知。