2011-08-12 24 views
4

假設您有一個搜索文本框並具有附加到TextChanged事件的搜索算法,該事件與BackgroundWorker一起運行。如果文本框中出現新字符,我需要取消先前的搜索並再次運行。重複使用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();   
    } 

回答

6

當_resetEvent.WaitOne()調用完成時,工作線程實際上並未完成。它正忙於從DoWork()返回並等待機會來運行RunWorkerCompleted事件(如果有的話)。這需要時間。

沒有可靠的方法來確保BGW以同步方式完成。在IsBusy上阻塞或等待RunWorkerCompleted事件運行將導致死鎖。如果你真的只想使用一個bgw,那麼你將不得不排隊請求。或者只是不要爲了小事而流汗,並分配另一個bgw。他們花費很少

+0

輝煌答案!! – Sandepku

1
  • 不要重複使用一個BackgroundWorker。這是一個便宜的資源,它不是一個線程。
  • 確保您的Bgw代碼停止,您的外觀確定。 Bgw將釋放線程到池中。
  • 但同時,爲新工作創建一個新的任務/ 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當代碼太正常完成。

+0

如果我在此函數中創建一個新的BackgroundWorker(),我該如何取消上一個?我應該把工作人員列入清單還是其他方面?由於搜索功能更新控件,我不希望多個工作人員更新相同的控件。 –

+0

@Carlos,你取消了舊的,並在創建新的之前等待_resetEvent。 –

1

我認爲你應該考慮不取消後臺工作。

如果取消請求並且用戶鍵入的速度比服務器返回查詢的速度快,那麼在完成鍵入之前,他將看不到建議。

在這樣的交互式場景中,顯示用戶輸入的內容可能會更好。如果他想要的單詞是您的建議列表,您的用戶將知道他可以停止打字。

這也將是爲您的服務器更好的時候,是忙碌的,因爲不是很多取消請求,誰將花費些什麼,但最終被未顯示,將有更少的請求,其響應你實際使用。

我遇到了(3d)渲染應用程序的類似問題,初學者的錯誤是在每次鼠標移動時取消並重新渲染。這導致了很多計算和很少的交互式反饋。

+0

+1謝謝,好點。我覺得需要取消,因爲我在後臺工作中更新了GUI,並且我不想讓多個工作人員更新相同的控件。 –