重用BackgroundWorker,取消并等待它

时间:2011-08-12 18:51:46

标签: c# multithreading backgroundworker

假设您有一个搜索文本框,并且有一个搜索算法附加到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;
            }
        }
    }

编辑以反映答案。不要重复使用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();        
    }

4 个答案:

答案 0 :(得分:7)

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

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

答案 1 :(得分: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

答案 2 :(得分:1)

  • 不要重复使用Backgroundworker。它是一个廉价的资源,它不是一个线程。
  • 确保您的Bgw代码停止,您的代码看起来不错。 Bgw会将线程释放到池中。
  • 但与此同时,为新工作创建一个新的任务/ Bgw。
  • 您可能想要取消订阅旧Bgw中的已完成事件。

答案 3 :(得分:1)

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

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

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

这对于您的服务器繁忙时也会更好,因为代替许多已取消的请求,谁将花费一些但最终未显示的请求,实际使用的响应将会减少。

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