【问题标题】:Reusing a BackgroundWorker, cancel and wait for it重用 BackgroundWorker,取消并等待它
【发布时间】:2011-08-12 18:51:46
【问题描述】:

假设您有一个搜索文本框,并且有一个附加到 TextChanged 事件的搜索算法,该算法与 BackgroundWorker 一起运行。如果文本框中出现新字符,我需要取消之前的搜索并重新运行。

我尝试在主线程和 bgw 之间使用事件,来自 this previous question,但我仍然收到错误“当前很忙,无法同时运行多个任务”

    BackgroundWorker bgw_Search = new BackgroundWorker();
    bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);

    private AutoResetEvent _resetEvent = new AutoResetEvent(false);

    private void txtSearch_TextChanged(object sender, EventArgs e)
    {
        SearchWithBgw();
    }

    private void SearchWithBgw()
    {
        // cancel previous search
        if (bgw_Search.IsBusy)
        {
            bgw_Search.CancelAsync();

            // wait for the bgw to finish, so it can be reused.
            _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
        }

        // start new search
        bgw_Search.RunWorkerAsync();   // error "cannot run multiple tasks concurrently"
    }

    void bgw_Search_DoWork(object sender, DoWorkEventArgs e)
    {
        Search(txtSearch.Text, e);
    }

    private void Search(string aQuery, DoWorkEventArgs e)
    {
        int i = 1;            
        while (i < 3)             // simulating search processing...
        {
            Thread.Sleep(1000);                           
            i++;

            if (bgw_Search.CancellationPending)
            {
                _resetEvent.Set(); // signal that worker is done
                e.Cancel = true;
                return;
            }
        }
    }

EDIT 反映答案。不要重用 BackgroundWorker,创建一个新的:

    private void SearchWithBgw()
    {   
        if (bgw_Search.IsBusy)
        {
            bgw_Search.CancelAsync();
            _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made

            bgw_Search = new BackgroundWorker();
            bgw_Search.WorkerSupportsCancellation = true;
            bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
        }

        bgw_Search.RunWorkerAsync();        
    }

【问题讨论】:

    标签: c# multithreading backgroundworker


    【解决方案1】:

    当 _resetEvent.WaitOne() 调用完成时,工作线程实际上并未完成。它正忙于从 DoWork() 返回并等待运行 RunWorkerCompleted 事件的机会(如果有)。这需要时间。

    没有可靠的方法来确保BGW以同步的方式完成。阻塞 IsBusy 或等待 RunWorkerCompleted 事件运行将导致死锁。如果您真的只想使用一个 bgw,那么您必须将请求排队。或者只是不要为小东西出汗并分配另一个bgw。它们的成本非常很少。

    【讨论】:

      【解决方案2】:

      如果旧的后台工作人员存在,则创建一个新的后台工作人员。

      private void SearchWithBgw()
      {
          // cancel previous search
          if (bgw_Search.IsBusy)
          {
              bgw_Search.CancelAsync();
      
              // wait for the bgw to finish, so it can be reused.
              _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
              BackgroundWorker bgw_Search = new BackgroundWorker();
              bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
      
      
          }
      
          // start new search
          bgw_Search.RunWorkerAsync();   // error "cannot run multiple tasks concurrently"
      }
      

      我也知道你输入了假代码,但你想确保在代码正常完成时设置_resetEvent

      【讨论】:

      • 如果我在这个函数中创建了一个new BackgroundWorker(),如何取消之前的?我应该把工人列在名单上吗?由于搜索功能会更新一个控件,我不希望多个工作人员更新同一个控件。
      • @Carlos,您取消旧的并等待它在创建新的之前将 _resetEvent 设置为单一。
      【解决方案3】:
      • 不要重复使用后台工作程序。它是一种廉价资源,它不是线程。
      • 确保您的 Bgw 代码停止,您的代码看起来正常。 Bgw 会将线程释放到池中。
      • 但与此同时,为新作业创建一个新任务/Bgw。
      • 您可能希望从旧 Bgw 取消订阅已完成的事件。

      【讨论】:

        【解决方案4】:

        我认为您应该考虑不取消后台工作人员。

        如果您取消请求并且用户输入的速度比您的服务器返回查询的速度快,那么他在完成输入之前不会看到建议。

        在这样的交互式场景中,最好显示用户输入内容后面的响应。如果他想到的词是您的建议列表,您的用户就会知道他可以停止输入。

        这对您的服务器在繁忙时也会更好,因为与许多取消的请求不同,这些请求会花费一些但最终没有显示出来,而您实际使用其响应的请求将会减少。

        我在 (3d) 渲染应用程序中遇到了类似的问题,初学者的错误是在每次鼠标移动时取消并重新渲染。这导致大量的计算和很少的交互式反馈。

        【讨论】:

        • +1 谢谢,好点子。我觉得有必要取消,因为我在后台更新 GUI,我不希望多个工作人员更新同一个控件。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-19
        相关资源
        最近更新 更多