嵌套的BackgroundWorkers:在错误的线程上运行RunWorkerCompleted?

时间:2010-11-24 00:28:20

标签: c# multithreading backgroundworker

我正在进行需要调用其他异步任务的异步操作。我试图通过使用BackgroundWorkers来保持简单,结果是一个BackgroundWorker的DoWork()回调调用一个创建第二个BackgroundWorker的方法,就像这样(减去错误检查和所有爵士乐的简洁):

class Class1
{
    private BackgroundWorker _worker = null;

    public void DoSomethingAsync()
    {
        _worker = new BackgroundWorker();
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
        _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
        _worker.RunWorkerAsync();
    }

    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        Class2 foo = new Class2();
        foo.DoSomethingElseAsync();
        while(foo.IsBusy) Thread.Sleep(0);  // try to wait for foo to finish.
    }

    void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // do stuff
    }
}

class Class2
{
    private BackgroundWorker _worker = null;
    Thread _originalThread = null;

    public AsyncCompletedEventHandler DoSomethingCompleted;

    public bool IsBusy { get { return _worker != null && _worker.IsBusy; } }

    public void DoSomethingElseAsync()
    {
        _originalThread = Thread.CurrentThread;

        _worker = new BackgroundWorker();
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
        _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
        _worker.RunWorkerAsync();
    }

    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        // do stuff
    }

    void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.Assert(Thread.CurrentThread == _originalThread);  // fails

        // Assuming the above Assert() were excluded, the following event would be raised on the wrong thread.
        if (DoSomethingCompleted != null) DoSomethingCompleted(this, new AsyncCompletedEventArgs(e.Error, e.Cancelled, null));
    }
}

所以问题是,我期望Class2._Worker_RunWorkerCompleted()在调用Class2.DoSomethingElseAsync()的同一个线程上执行。这种情况从未发生过 - 相反,回调运行在一个全新的线程上。

这是我的怀疑:Class1的_worker_DoWork()永远不会返回,这意味着线程永远不会回到事件监听器,即使存在一个(我怀疑没有)。另一方面,如果_worker_DoWork()确实返回,Class1的BackgroundWorker会自动提前完成 - 它需要等待Class2完成工作才能完成其工作。

这导致两个问题:

  1. 我的怀疑是否正确?
  2. 嵌套这样的异步操作的最佳方法是什么?我可以挽救BackgroundWorker方法,还是有其他更合适的技术?

3 个答案:

答案 0 :(得分:5)

如果在UI线程上创建BackgroundWorkerDoWork将在线程池线程上运行,RunWorkerCompleted将在UI线程上运行。

如果在后台线程上创建BackgroundWorker(即不是UI线程),DoWork仍将在线程池线程上运行,RunWorkerCompleted也将在线程池线程上运行

在您的情况下,由于您无法封送对任意(线程池)线程的调用,您将无法保证所需的行为,尽管您可能需要查看{{1} }。

答案 1 :(得分:0)

您应该使用ManualResetEvent在线程之间进行通信:

http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent%28VS.71%29.aspx

答案 2 :(得分:0)

首先,我看不到任何实际开始运行工作人员的地方。您可以更改DoSomethingAsync方法(也可以在Class2中添加对DoSomethingElseAsync方法的调用)

public void DoSomethingAsync()
{
    _worker = new BackgroundWorker();
    _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);
    _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
    _worker.RunWorkerAsync(); // add this line to start it
}

其次,工作处理程序(_worker_DoWork方法)不能保证与调用DoSomethingAsync在同一个线程上 - 这是后台工作者的全部要点。 ie /在另一个线程上工作。这同样适用于worker完成处理程序(_worker_RunWorkerCompleted方法)。

最后,附加两个不同的后台工作者似乎没有意义,除非顶级一级(Class1)总是要求Class2工作。你最好让一个经理来处理每个后台员工。