【问题标题】:C# Is it safe to call 'BackgroundWorker' inside 'using' statement?C# 在“using”语句中调用“BackgroundWorker”是否安全?
【发布时间】:2015-02-03 20:15:54
【问题描述】:

我开发了一个 Windows 服务。在服务中,我使用 BackgroundWorker 在我的数据库中发布数据。 我在我的数据库构造函数类中声明了一个 BackgroundWorker,并在需要时使用它。 在测试过程中,我遇到了一个错误:

此 BackgroundWorker 当前正忙,无法运行多个任务 同时

我试图找出解决方案,许多人建议为每个任务使用新实例。我改变了我的代码:

        ...
        using (BackgroundWorker bw = new BackgroundWorker())
        {
            bw.DoWork += new DoWorkEventHandler(bkDoPost);
            bw.RunWorkerAsync(dbobj);
        }
        ...

而我的“bkDoPost”是:

void bkDoPost(object sender, DoWorkEventArgs e)
{
    try
    {
        dbObject dbj = e.Argument as dbObject;
        this.db.Insert(dbj.tableName, dbj.data);
    }
    catch (Exception ex)
    {
        logs.logMessage("There was an error in data post. See the ErrorLog");
        logs.logError(ex);
    }
}

代码在测试过程中运行良好。

我的问题是我的方法正确吗?

或者这样做有什么问题吗?

谢谢

【问题讨论】:

    标签: c# windows-services backgroundworker


    【解决方案1】:

    不要那样做。您的后台工作人员将在您的工作完成之前被处置。

    工作完成后最好手动调用Dispose

    更好的是,考虑使用不同的方案来处理异步工作。后台工作者正在变得过时并且针对 UI 应用程序,而不是服务。对并行操作的限制突出了类的意图。

    【讨论】:

      【解决方案2】:

      不要将 BackgroundWorker 放入 using 语句中。而是将 Dispose() 调用放入 RunWorkerCompleted 事件中。

      尽管如此,BackgroundWorker 可能不是在您的情况下使用的最佳选择,因为它的主要用途是在 UI 保持响应时运行一些业务代码,并在 RunWorkerCompeleted 事件中自动更新 UI。

      如果您在作业完成时不需要干预 UI,或者您有很多较小的作业要完成,那么切换到将您的作业封装在 Tasks 中会更有效。

      【讨论】:

      • 感谢@Oliver,这是客户端应用程序使用的服务。当它收到请求时,它会将信息记录到数据库并将响应返回给客户端请求。数据库驻留在远程服务器上,因此发布数据可能需要更长的时间,这就是我决定在后台发布数据的原因。
      • @lqbal:然后切换到Task。
      【解决方案3】:

      如果您有许多更新,则为每个更新创建一个 BackgroundWorker 可能会非常耗时且耗费内存。
      我会使用一个独立的线程,每次必须完成更新时我都会唤醒它:

      Queue<DbWork> dbUpdates = new Queue<DbWork>();
      EventWaitHandle waiter = new EventWaitHandle(false, EventResetMode.ManualReset);
      ...
      // Init :
      new Thread(new ThreadStart(DbUpdateWorker));
      ...
      
      private void DbUpdateWorker()
      {
          while (true)
          {
              DbWork currentWork = null;
              lock (dbUpdates)
              {
                  if (dbUpdates.Count > 0)
                      currentWork = dbUpdates.Dequeue();
              }
      
              if (currentWork != null)
              {
                  currentWork.DoWork();
              }
      
              if (dbUpdates.Count == 0)
              {
                  waiter.WaitOne();
              }
          }
      }
      
      public void AddWork(DbWork work)
      {
          lock (dbUpdates)
          {
              dbUpdates.Enqueue(work);
          }
          waiter.Set();
      }
      

      【讨论】:

      • 创建新对象不是性能问题,除非您遇到真正疯狂的数字。算术和 IO 是最大的障碍。当前的趋势是为每个工作单元创建一个新的Task。创建Task 涉及从操作系统租用信号量,但性能是可以接受的。为什么是这样?它们封装的异步工作通常要贵几个数量级 - 相比之下,设置时间很便宜。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-05
      • 1970-01-01
      • 2010-11-22
      • 1970-01-01
      • 2010-10-11
      相关资源
      最近更新 更多