【问题标题】:How do I know when multiple background workers are finished?我如何知道多个后台工作人员何时完成?
【发布时间】:2017-06-06 20:09:34
【问题描述】:

所以现在我有一个方法可以调用 3 次不同的方法。

List<BackgroundWorker> bgws = new List<BackgroundWorker>();
AutoResetEvent _workerCompleted = new AutoResetEvent(false);

void methodA () {
    methodB(); //bw1
    methodB(); //bw2
    methodB(); //bw3

    //wait for the background workers that were kicked off above to finish
    _workerCompleted.WaitOne();

    Console.Writeline("hey");
}

void methodB() {
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = true;
    bw.WorkerSupportsCancellation = true;
    bgws.Add(bw);

    bw.DoWork += (sender, DoWorkEventArgs) => { bwWork(sender, DoWorkEventArgs, info); };
    bw.ProgressChanged += (sender, ProgressChangedEventArgs) => { bwProgressChanged(sender, ProgressChangedEventArgs, info); };
    bw.RunWorkerCompleted += (sender, RunWorkerCompletedEventArgs) => { bwCompleted(sender, RunWorkerCompletedEventArgs, info); };

    bw.RunWorkerAsync();

}

bwWorkbwProgressChanged 事件不应该太重要。前者只是处理一个文件,后者是更新一个进度条和标签。

private void bwCompleted(object senderr, 
System.ComponentModel.RunWorkerCompletedEventArgs e, SenderInfo info)
{
    BackgroundWorker bw = (BackgroundWorker)senderr;

    Console.WriteLine(Path.GetFileName(info.path) + " : Finished.");

    bgws.Remove(bw); //remove the bw from the list
    //remove events from bw
    bw.DoWork -= (sender, DoWorkEventArgs) => { bwWork(sender, DoWorkEventArgs, info); };
    bw.ProgressChanged -= (sender, ProgressChangedEventArgs) => { bwProgressChanged(sender, ProgressChangedEventArgs, info); };
    bw.RunWorkerCompleted -= (sender, RunWorkerCompletedEventArgs) => { bwCompleted(sender, RunWorkerCompletedEventArgs, info); };
    //dispose bw
    bw.Dispose();
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    if (bgws.Count == 0) //if there are no more bw's, we're good to go
    {
        MessageBox.Show("Done.");
        bgws.Clear();
        _workerCompleted.Set();
    }

}

所以最后,我希望所有后台工作人员在打印出“嘿”之前完成他们正在做的事情。我尝试使用 AutoResetEvent 和 ManualResetEvent,但是当它们出现时似乎什么都没有发生。我的意思是,当我包含后台工作人员时,他们甚至都没有运行。

我会调用一个线程,但我有点不情愿,因为有人告诉我后台工作人员是在执行后台工作和更新 UI 时使用的东西。有什么方法可以让我等待三个后台工作人员,以便我可以继续执行我的程序?

【问题讨论】:

  • 您是否无法让后台工作人员实际执行任务,或者您是否无法与所有工作人员同步并检测所有工作何时完成?我不清楚你具体遇到了什么问题。
  • 很抱歉。所以如果我在程序中包含AutoResetEvent 部分,后台工作人员都不会做任何事情。如果我不包括它,后台工作人员工作得很好,但Console.WriteLine("hey"); 是在工作人员完成他们正在做的事情之前执行的。我想我可以在检查后台工作人员列表是否为空的部分中添加更多代码,但是我还有更多代码要做,而且我觉得这会扰乱程序的流程。我只想让后台工作人员做他们的事情,然后继续处理 writeline 语句所在的位置。
  • 澄清:后台工作人员不做任何事情(即bwWork 永远不会被执行),或者他们做他们的工作,但从不发出完全完成的信号,所以你的代码永远等待?
  • 您似乎在 bwCompleted 中做了一些不必要的工作 - 为什么要在您即将访问对象的 Dispose 时删除事件?为什么Clear()bgws列表你已经清楚了?
  • 有趣。我很高兴你指出了这一点。 bwWork 是当AutoResetEvent 部分在程序中时执行的三个事件中的唯一一个。由于某种原因,进度没有改变,完成的事件也没有被执行。

标签: c# multithreading backgroundworker


【解决方案1】:

我认为问题在于 BackgroundWorker 在单独的线程上执行 DoWork,但使用创建它的线程来运行 ProgressChangedRunWorkerCompleted 事件,以便他们可以更新 UI。但是,如果您已经将 UI 线程绑定在 WaitOne 或其他自旋中,则无法执行这些事件。

在您的bwCompleted 中,当bgws.Count == 0 时,您为什么不调用methodA_AfterDone 来处理methodA 的读取,并且只在bw3 之后让methodA 结束?

【讨论】:

  • 啊,我想这是一种方法。我真的很想继续我最初发布 bw 的地方,但如果我不能,那也没关系。
  • @Tim 或者,您可以同时执行这两种操作:保留后台工作人员,但创建尽可能多的 TaskCompletionSource,并在 RunWorkerCompleted 事件中将它们转换为已完成。从那里,您可以在任务列表中等待 Task.WhenAll,然后从您开始的地方继续
  • @KevinGosse 我的意思是你可以,但是当你可以只使用Task.Run 时,你为什么还要费心重新创建自己的效率较低的Task.Run 实现。
  • @Servy 我刚刚搜索并了解了Progress&lt;T&gt;,我不知道 TPL 有报告进度的方法。从这个角度来看,确实没有必要坚持使用 BackgroundWorker
【解决方案2】:

知道我迟到了两年,但这就是我在类似情况下所做的。 创建了一个完整的计数器。 每次列表中的BGW 完成时,都会向完成的计数器添加一个。 有一个单独的 MonitorBGW 设置,其中有一个类似于以下内容的 while 循环。

While(CompletedCounter < ListBGWs.Count){Thead.Sleep(2000)}
//Put my code I wanted run after they all completed here.

希望这对某人有帮助:)

【讨论】:

    【解决方案3】:

    很简单,您不使用 BackgroundWorker 并切换到 Tasks。 Task.Run(() =&gt; Your work ); 然后你可以使用Task.WaitAll(task1,task2); 你可以切换任何EAP to Tasks。并且使用 async 你可以轻松地做到这一点。

    【讨论】:

    • 任务不保证任务在不同的线程上运行 - 它可能在同一个线程上运行,因此它可能会占用 UI 线程。
    • 任务不会,但 'Task.Run' 将在不同的线程上运行并且不会阻塞 UI。正如我所说,您可以使用 Task 包装 BackgroundWorker,以便他可以使用 Task.WaitAll 或 Task WhenAll
    • Tasks 会保证进度事件在 UI 线程上运行吗?
    • @NetMage TPL 中有这样做的工具,是的。
    • @NetMage TPL 是多年来进行长期后台操作的推荐方式。有关进度,您可以查看this
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-06
    • 2014-03-12
    相关资源
    最近更新 更多