重用BackgroundWorker,取消并等待它

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

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

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

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

如果旧的后台工作程序存在,则创建一个新后台工

 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

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

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

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

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

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

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