简体   繁体   English

终止多个后台工作人员

[英]termination of multiple background workers

I have a main thread that invokes multiple backgroundworkers (in .net/c#). 我有一个调用多个后台工作程序(在.net / c#中)的主线程。 Each of these threads starts a process in order to run an executable. 这些线程中的每个线程都会启动一个进程以运行可执行文件。 When a process ends, I want to tell the main thread to kill all other threads and their respective processes. 当进程结束时,我想告诉主线程杀死所有其他线程及其各自的进程。 After all of them stopped, I want to know this and continue to run the main thread for post-processing. 他们都停止之后,我想知道这一点,并继续运行主线程进行后处理。
I keep a list of these external processes so I have no problem killing them all. 我保留了这些外部进程的列表,因此我完全可以消除它们。 My problem is how to kill all these backgroundworkers. 我的问题是如何杀死所有这些背景工作人员。 I tried to keep a list of the threads associated with them and kill them from within the first thread that terminates, but apparently this does not kill the backgroundworker itself because the runworkercompleted method is still invoked multiple times. 我试图保留一个与它们关联的线程的列表,并从终止的第一个线程中杀死它们,但是显然,这并没有杀死backgroundworker本身,因为runworkercompleted方法仍被多次调用。 Does anyone have a pattern on how to kill those workers in a nice way ? 有没有人有一个很好的杀人方式的模式? should I somehow notify the main thread to do the killing of the other workers ? 我应该以某种方式通知主线程杀死其他工人吗?

I'd recommend using async/await and CancellationTokenSources. 我建议使用async / await和CancellationTokenSources。 I'll give advice on how to use BackgroundWorkers as well, but since async/await is so much more convenient (and shorter), it goes first. 我还将提供有关如何使用BackgroundWorkers的建议,但是由于async / await更加方便(且更短),因此首先。

async/await is convenient because it gives you the features you're looking for without much added complexity. 异步/等待很方便,因为它可以为您提供所需的功能,而不会增加太多复杂性。

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
}

Your "waiters" look something like this: 您的“服务员”看起来像这样:

private async Task DoSomething(CancellationToken token)
{
    // Do stuff

    // Periodically check if someone has finished
    if (Token.IsCancellationRequested)
    {
        // clean up
        return;
    }
}

async/await code has a few gotchas, including deadlock . 异步/等待代码有一些陷阱, 包括死锁 Since this sounds like a quick project (I could be wrong), it seems like a good place to learn - especially if there's no massive codebase to rework. 由于这听起来像是一个快速的项目(我可能错了),所以它似乎是一个学习的好地方-尤其是在没有大量代码库需要重做的情况下。 If you want to learn more, I think Stephen Cleary's blog is a good place to start, particularly his intro . 如果您想了解更多信息,我认为Stephen Cleary的博客是一个不错的起点,尤其是他的介绍

On the other hand, if you're absolutely sure you want to use BackgroundWorkers... well I don't blame you, but I don't envy you either. 另一方面,如果您绝对确定要使用BackgroundWorkers ...好吧,我不怪您,但我也不嫉妒您。

First, your workers have to know whether somebody else finished first. 首先,您的工人必须知道别人是否先完成工作。 Use the finished BackgroundWorker's RunWorkerCompleted method to cancel the others 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...
    }
}

Then, add a bit of code at the end of your DoWork method to report the cancellation... 然后,在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;        
}

And that's it. 就是这样。 You can also check the worker.CancellationPending periodically to see if you can finish earlier, but don't forget to assign worker.CancellationPending to e.Cancel before your return! 您还可以定期检查worker.CancellationPending,以查看是否可以更早完成,但不要忘记在返回之前将worker.CancellationPending分配给e.Cancel!

One last thing: if you want the postprocessing to continue when all workers have finished (and only then), you need to have a way to mark when a particular worker is finished (to cancel the others), and then a way to find out when they've all finished (so you can begin postprocessing). 最后一件事:如果您希望后处理在所有工作人员都已完成时(并且仅在此之后)继续进行,您需要有一种方法来标记特定工作人员的完成时间(以取消其他工作人员),然后通过一种方法找出当它们全部完成后(以便您可以开始后处理)。 It's doable, and not too difficult - off the top of my head, I'd use a Dictionary<BackgroundWorker, bool> to indicate which workers have finished. 这是可行的,而且不太困难-浮现在脑海中,我会使用Dictionary<BackgroundWorker, bool>来指示哪些工人已经完成。 Still, that's another piece of clutter you can avoid with async/await. 尽管如此,使用async / await还是可以避免的另一混乱情况。

From this answer it seems there is no way to kill a Backgroundworker. 这个答案看来,没有办法杀死Backgroundworker。 However this answer shows a workaround by overriding OnDoWork and keeping a reference to Thread.CurrentThread . 但是, 此答案通过覆盖OnDoWork并保留对Thread.CurrentThread的引用显示了一种解决方法。 I would still try to have those Backgroundworkers check for a notification to cancel, though. 不过,我仍然会尝试让那些Backgroundworkers检查取消通知。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM