【问题标题】:termination of multiple background workers终止多个后台工作人员
【发布时间】:2017-08-22 21:26:11
【问题描述】:

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

【问题讨论】:

  • 如果您将发布一些有关如何创建和启动线程/进程的代码将会很有帮助

标签: c# multithreading backgroundworker


【解决方案1】:

我建议使用 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> 来指示哪些工人已经完成。不过,这是你可以通过 async/await 避免的另一块混乱。

【讨论】:

    【解决方案2】:

    this answer看来,没有办法杀死后台工作者。然而this answer 通过覆盖OnDoWork 并保留对Thread.CurrentThread 的引用显示了一种解决方法。不过,我仍然会尝试让那些后台工作人员检查是否有要取消的通知。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-14
      相关资源
      最近更新 更多