重用BackgroundWorker,取消并等待它

本文关键字:等待 取消 BackgroundWorker 重用 | 更新日期: 2023-09-27 18:05:19

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

我尝试在主线程和bgw之间使用事件,从上一个问题,但我仍然得到错误"当前忙,不能同时运行多个任务"

    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();        
    }

重用BackgroundWorker,取消并等待它

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

没有可靠的方法来确保BGW以同步方式完成。阻塞IsBusy或等待RunWorkerCompleted事件运行将导致死锁。如果您真的只想使用一个bgw,那么您必须将请求排队。或者不要为小事烦恼,再分配一笔钱。

如果旧的后台worker存在,则创建一个新的。

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

    不要重用后台工作线程。它是一个廉价的资源,它不是一个线程。
  • 确保你的Bgw代码停止,你的代码看起来没问题。Bgw将把线程释放到池中。
  • ,同时为新作业创建一个新的Task/Bgw。
  • 您可能想要从旧的Bgw中取消订阅已完成的事件。

我认为你应该考虑不要取消后台worker。

如果你取消请求,而用户键入的速度比服务器返回查询的速度快,他将不会看到建议,直到他完成键入。

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

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

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