代码之家  ›  专栏  ›  技术社区  ›  Sean U

嵌套的BackgroundWorkers:在错误线程上运行WorkerCompleted调用?

  •  4
  • Sean U  · 技术社区  · 14 年前

    我正在研究异步操作,它需要调用更多的异步任务。我试图通过使用BackgroundWorker s来保持它的简单性,结果是一个BackgroundWorker的DoWork()回调调用了一个方法,该方法创建了另一个BackgroundWorker,如so(减去错误检查和所有jazz以获得简洁性):

    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.DoSomethingElseAsync()的同一线程上执行Class2。这种情况永远不会发生-相反,回调在一个全新的线程上运行。

    这是我的怀疑:Cyr1的AyWorksJordOrk()不返回,这意味着线程永远不会回到事件侦听器,即使存在一个(我怀疑一个也没有)。另一方面,如果worker()返回,Class1的BackgroundWorker将自动提前完成——它需要等待Class2完成工作,然后才能完成工作。

    这引出两个问题:

    1. 我的怀疑是对的吗?
    2. 嵌套这种异步操作的最佳方法是什么?我是否可以挽救后台工作方法,或者有其他更合适的技术?
    3 回复  |  直到 14 年前
        1
  •  5
  •   lesscode    9 年前

    如果 BackgroundWorker 是在UI线程上创建的, DoWork 将在线程池线程上运行,并且 RunWorkerCompleted 将在UI线程上运行。

    如果 后台工作人员 是在后台线程上创建的(即不是UI线程) 嫁妆 仍将在线程池线程上运行,并且 RunWorker已完成 也将在线程池线程上运行。

    在您的例子中,由于您无法封送对任意(线程池)线程的调用,因此您将无法保证所需的行为,尽管您可能需要查看 System.Threading.SynchronizationContext .

        2
  •  0
  •   dexter    14 年前
        3
  •  0
  •   Tim Carter    14 年前

    首先,我看不到任何地方真正开始运行工人。您可以更改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的调用在同一个线程上—这是后台工作程序的全部要点。在另一条线上工作。这同样适用于worker complete处理程序(runworker completed方法)。

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