【问题标题】:Synchronize background worker同步后台工作者
【发布时间】:2013-10-03 07:36:53
【问题描述】:

我有这样的代码:

if (condition#1) {
   // step 2
   ConfigurazioneSA csa = new ConfigurazioneSA(...);
   WconfiguraSA.RunWorkerAsync(csa);
}
else
{
   // step 1
   PassaggioIE bo = new PassaggioIE(...);
   WpassIE.RunWorkerAsync(bo);

   if (condition#2) {
      // step 2
      ConfigurazioneSA csa = new ConfigurazioneSA(...);
      WconfiguraSA.RunWorkerAsync(csa);
   }
}

当执行流程在condition#1 分支内时,step 1 块之前已被执行(在我的应用程序的之前执行中),因此step 2 可以毫无问题地执行。

但是,当执行流程在else 分支内时,我需要执行step 1,当这一步完成后,我可以执行step 2。由于我使用BackgroundWorker 作为第一步开始,step 2在导致错误后立即开始。

我会同步这个生产者/消费者问题,尽可能减少熵。我发现的所有解决方案都引入了大量代码,而我会使用一个简单的信号量来避免 step 2 执行,直到 step 1 未完成。有什么想法吗?

【问题讨论】:

    标签: c# backgroundworker .net-4.5


    【解决方案1】:

    我建议您为此使用Tpl。这很容易实现。

        Task.Factory.StartNew(() =>
        {
            //Step1
        }).ContinueWith(antecedent =>  //executed after step1 is done
        {
            if (condition#2) 
            {
            // step 2
            }
        });
    

    还请注意,您不会收到像 ProgressCompleted 这样的事件,您必须做出牺牲。 Completed 事件类似于ContinuationTask

    这里的另一个区别是ContinuationTask 将在线程池线程中执行,如果你想在 UI 线程中使用TaskScheduler.FromCurrentSynchronizationContext

    【讨论】:

    • 我已将您的代码插入到我的 else 分支中,然后将 step1 和 step2 替换为真实代码,但似乎已经不同步:(
    • 我找不到你,会发生什么?
    • 什么都没有。一旦 step1 开始 step2 也开始(导致与以前相同的错误)...
    • 将 step1 和 step2 替换为 DoWork 处理程序中的代码,而不是 RunWorkerAsync
    • 是的 unarist 是对的,您必须在 step1 中使用 DoWork 中的代码
    【解决方案2】:

    IsBusy 示例:

    else
    {
       // step 1
       PassaggioIE bo = new PassaggioIE(...);
       WpassIE.RunWorkerAsync(bo);
    
       // wait for WpassIE to complete
       while(WpassIE.IsBusy) Application.DoEvents();
    
       if (condition#2) {
          // step 2
          ConfigurazioneSA csa = new ConfigurazioneSA(...);
          WconfiguraSA.RunWorkerAsync(csa);
       }
    }
    

    在此解决方案中,您必须等待 WpassIE 完成。

    不要使用Thread.Sleep 代替Application.DoEvents,因为它会导致类似BackgroundWorkers never stop being busy 的问题。

    【讨论】:

      【解决方案3】:

      如果您不想使用 Tasks,可以使用在其执行完成时引发的 BackgroundWorker.RunWorkerCompleted 事件。

      // step 1
      PassaggioIE bo = new PassaggioIE(...);
      
      WpassIE.RunWorkerCompleted += (sender, e) => {
      
          // You might want to add error handling code here.
      
          if (condition#2) {
              // step 2
              ConfigurazioneSA csa = new ConfigurazioneSA(...);
              WconfiguraSA.RunWorkerAsync(csa);
          }
      }
      
      WpassIE.RunWorkerAsync(bo);
      

      但我认为使用 TPL 也更好。

      【讨论】:

      • bo.RunWorkerCompleted 是否会与之前的 RunWorkerCompleted 造成混乱?
      • 糟糕,我以为bo 是BackgroundWorker...如果你多次使用同一个BackgroundWorker,那就对了。
      • 对于解决方法,将 lambda 表达式保存为委托,然后您可以将其从 RunWorkerCompleted 中删除。如果您可以在 step1 时阻止此方法,BackgroundWorker.IsBusy 很有用。
      • 我应该写像while(!BackgroundWorker.IsBusy) {// step 2}这样的东西
      • 喜欢while(!BackgroundWorker.IsBusy) Application.DoEvents();。见stackoverflow.com/questions/2183520/…
      猜你喜欢
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      • 2014-10-02
      • 1970-01-01
      • 2011-10-30
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多