如何在BackgroundWorker完成之前正确等待?

mar*_*ark 37 .net multithreading backgroundworker

请注意以下代码:

var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnAsyncOperationCompleted;
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
Run Code Online (Sandbox Code Playgroud)

现在假设我想等到bw完成工作.这样做的正确方法是什么?

我的解决方案是:

bool finished = false;
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
  OnAsyncOperationCompleted(sender, args);
  finished = true;
});
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
int timeout = N;
while (!finished && timeout > 0)
{
  Thread.Sleep(1000);
  --timeout;
}
if (!finished)
{
  throw new TimedoutException("bla bla bla");
}
Run Code Online (Sandbox Code Playgroud)

但我不喜欢它.

我已经考虑用finished同步事件替换标志,在RunWorkerCompleted处理程序中设置它并在以后阻塞它而不是执行while-sleep循环.

唉,这是错误的,因为代码可能在WPF或WindowsForm同步上下文中运行,在这种情况下,我会阻止与RunWorkerCompleted处理程序运行相同的线程,这显然不是非常聪明的举动.

我想知道一个更好的解决方案.

谢谢.

编辑:

PS

  • 示例代码是故意设计的,以澄清我的问题.我完全知道完成回调,但我想知道如何等到完成.这是我的问题.
  • 我知道Thread.Join,Delegate.BeginInvoke,ThreadPool.QueueUserWorkItem,等...问题是,特别是约BackgroundWorker.

编辑2:

好吧,我想如果我解释这个场景会更容易.

我有一个单元测试方法,它调用一些异步代码,这反过来最终会使BackgroundWorker我能够传递一个完成处理程序.所有的代码都是我的,所以如果我愿意,我可以改变实现.但是,我不会更换BackgroundWorker,因为它会自动使用正确的同步上下文,因此当在UI线程上调用代码时,在同一UI线程上调用完成回调,这非常好.

无论如何,单元测试方法有可能在BW完成其工作之前结束,这是不好的.所以我希望等到BW完成并想知道它的最佳方法.

它有更多的部分,但整体情况或多或少像我刚才描述的那样.

Joh*_*esH 44

尝试使用AutoResetEvent类,如下所示:

var doneEvent = new AutoResetEvent(false);
var bw = new BackgroundWorker();

bw.DoWork += (sender, e) =>
{
  try
  {
    if (!e.Cancel)
    {
      // Do work
    }
  }
  finally
  {
    doneEvent.Set();
  }
};

bw.RunWorkerAsync();
doneEvent.WaitOne();
Run Code Online (Sandbox Code Playgroud)

注意:doneEvent.Set()无论发生什么情况,都应该确保调用它.此外,您可能希望提供doneEvent.WaitOne()指定超时期限的参数.

注意:此代码几乎是Fredrik Kalseth类似问题的回答.


Azh*_*any 19

要等待后台工作线程(单个或多个),请执行以下操作:

  1. 创建以编程方式创建的后台工作程序列表:

    private IList<BackgroundWorker> m_WorkersWithData = new List<BackgroundWorker>();
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在列表中添加后台worker:

    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
    worker.WorkerReportsProgress = true;
    m_WorkersWithData.Add(worker);
    worker.RunWorkerAsync();
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用以下函数等待List中的所有worker:

    private void CheckAllThreadsHaveFinishedWorking()
    {
        bool hasAllThreadsFinished = false;
        while (!hasAllThreadsFinished)
        {
            hasAllThreadsFinished = (from worker in m_WorkersWithData
                                     where worker.IsBusy
                                     select worker).ToList().Count == 0;
            Application.DoEvents(); //This call is very important if you want to have a progress bar and want to update it
                                    //from the Progress event of the background worker.
            Thread.Sleep(1000);     //This call waits if the loop continues making sure that the CPU time gets freed before
                                    //re-checking.
        }
        m_WorkersWithData.Clear();  //After the loop exits clear the list of all background workers to release memory.
                                    //On the contrary you can also dispose your background workers.
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 我在这里忘记提到的另一件事是因为你将事件添加为+ = ...你必须这样做 - =当后台工作人员完成执行时,否则后台工作人员将不会处理. (2认同)

Jef*_*cox 7

BackgroundWorker有一个完成事件.而不是等待,从完成处理程序调用剩余的代码路径.

  • 我知道它,代码片段演示了它.但问题是等待. (10认同)