2011-08-12 24 views
4

検索テキストボックスがあり、BackgroundWorkerで実行されるTextChangedイベントに検索アルゴリズムが関連付けられているとします。テキストボックスに新しい文字が来る場合は、前の検索をキャンセルしてもう一度実行する必要があります。BackgroundWorkerを再利用し、キャンセルしてそれを待つ

私はthis previous questionから、メインスレッドと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();   
    } 

答えて

6

_resetEvent.WaitOne()呼び出しが完了すると、ワーカースレッドは実際には実行されません。 DoWork()から復帰し、RunWorkerCompletedイベントがあればそれを実行する機会を待っています。それには時間がかかる。

BGWが同期的に完了することを確実にする方法はありません。 IsBusyをブロックしたり、RunWorkerCompletedイベントが実行されるのを待つと、デッドロックが発生します。 が本当にの場合は、1つのbgwだけを使用したい場合は、リクエストをキューに入れる必要があります。あるいは、小さなものを汗ばませたり、別のbgwを割り当てたりしないでください。彼らは非常に少しです。

+0

素晴らしい答えです! – Sandepku

1
  • のBackgroundWorkerを再利用しないでください。これは安価なリソースであり、スレッドではありません。
  • Bgwコードが停止していることを確認してください。 Bgwはスレッドをプールに解放します。
  • ただし、新しいジョブ用の新しいタスク/ Bgwを作成してください。
  • Completedイベントを古い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

この関数内で 'new BackgroundWorker()'を作成した場合、前のものを取り消すにはどうしたらいいですか?労働者をリストに入れておくべきですか?検索機能はコントロールを更新するので、複数のワーカーが同じコントロールを更新する必要はありません。 –

+0

@Carlosでは、古いものを取り消し、新しいものを作成する前にその_resetEventを単一にするのを待ちます。 –

1

バックグラウンドワーカーをキャンセルしないようにしてください。

リクエストをキャンセルし、サーバーがクエリを返すよりもユーザーの種類が速い場合、入力が完了するまで候補は表示されません。

このようなインタラクティブなシナリオでは、ユーザーが入力したもので後ろに続く応答を表示する方がよい場合があります。あなたが気づいた言葉があなたの提案リストである場合、あなたのユーザーは入力をやめることができます。

多くのキャンセルされたリクエストの代わりに、何人かの費用がかかりますが、最終的には表示されないため、実際に使用しているレスポンスの数が少なくなるため、これはビジー状態のサーバーにとっても優れています。

私は(3d)レンダリングアプリケーションで同様の問題に遭遇しました。初心者の間違いはすべてのマウスでキャンセルして再レンダリングすることです。これは、多くの計算とほとんどインタラクティブなフィードバックにつながりません。

+0

+1ありがとう、良い点。バックグラウンドでGUIを更新したので、複数のワーカーが同じコントロールを更新しないようにしたいので、取り消す必要があると感じました。 –

関連する問題