2012-03-08 9 views
0

私はBackgroundWorkerを使用して一定の間隔でping操作を実行するツールを開発しています。 BackgroundWorker ProgressChangedイベントに関する問題が発生しています。 ProgressChangedイベントのコードは、以下である:pingが終了したときBackgroundWorkerとstatusstrip updateでのクロススレッドの問題

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      ProgressUpdated update = (ProgressUpdated)e.UserState; 
      if (sender.ToString() == "System.ComponentModel.BackgroundWorker") 
      { 
       toolStripStatusLabel1.Text = update.GeneralStatus; 
       toolStripProgressBar1.Value = update.ProgressStatus; 
       toolStripStatusLabel2.Text = update.SpecificStatus; 
      } 
      else 
      { 
       toolStripStatusLabel1.Text = update.GeneralStatus; 
       toolStripProgressBar2.Value = update.ProgressStatus; 
       toolStripStatusLabel3.Text = update.SpecificStatus; 
      } 
     } 

ProgressChangedイベントは両方それが最初の値を更新BackgroundWorkおよびpingcompletedcallbackイベントから呼び出されます。 ProgressChangedイベントがPingCompletedCallbackイベントから実行されるときにのみ、クロススレッドの問題に遭遇します。 2番目の進捗バーを更新すると、エラーがスローされます。

私はなぜコールの1つではなく、他のもので起こっているのか理解できないようです。

BackgroundWorkerスレッドでPingCompletedCallBackが起きているのですが、それが原因でクロススレッドの問題が発生するのはなぜですか?

もしそうなら、イベントを発生させてUIスレッドで処理し、バックグラウンドワーカーではないようにするにはどうすればよいですか?

編集:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker worker = sender as BackgroundWorker; 
     // creates ping and sends it async 
     ProgressUpdated args = new ProgressUpdated(string1, int1, string 2); 
     worker.ReportProgress(0,args); 
     // rest of thread for cleanup when cancellation is called 
    } 
private void PingCompletedCallback(object sender, PingCompletedEventArgs e) 
    { 
      // handle the ping response 
      ProgressUpdated update = new ProgressUpdated(string1, int1, string2); 
      ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update); 
      backgroundWorker1_ProgressChanged(this, changed); 
      // handle other types of responses 

    } 

私はイベントの使用は、スレッドの分離を可能にすることだと思いました。別名ワーカースレッドは、UIスレッドがリッスンしているイベントを発生させ、発生したイベントはUIスレッドで処理されます。

私の理解が間違っていたため、PingCompletedCallBackはバックグラウンドワーカーのReportProgressメソッドにアクセスできますか?

私はその後PingCompletedCallbackに変更することができます:

ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update); 
backgroundWorker1_ProgressChanged(this, changed); 

へ:

backgroundWorker1.ReportProgress(1, update); 

または私はいくつかの他の方法でそれを変更する必要があるでしょうか?

ありがとうございました。

編集2:

私は、私は残っている唯一のことはしてPingCompletedCallbackから新しいイベントを呼び出すことです

private void PingUpdate (object sender, ProgressUpdated e) 
    { 
     toolStripStatusLabel1.Text = e.GeneralStatus; 
     toolStripProgressBar2.Value = e.ProgressStatus; 
     toolStripStatusLable3.Text = e.SepcificStatus; 
    } 

第2の更新イベントを作成し

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      ProgressUpdated update = (ProgressUpdated)e.UserState; 
      toolStripStatusLabel1.Text = update.GeneralStatus; 
      toolStripProgressBar1.Value = update.ProgressStatus; 
      toolStripStatusLabel2.Text = update.SpecificStatus; 
     } 

変更ProgrssChangedイベントUIスレッドで実行されるような方法です。 Invokeステートメントを使用する場所か、新しいイベントでInvokeを使用する必要がありますか?

+0

サイドノート:)sender.ToStringより( "送信者がBackgroundWorkerのである" と言った方が良いです。 ProgressChangedはUIスレッドで実行されるため、Invokeは必要ありません。 –

+0

backgroundWorker1_ProgressChangedメソッドで何らかの呼び出しを使用する必要はありません。 ReportProgressを使用して進捗状況を報告します。これは、これらのバックグラウンドワーカーの目標です。バックグラウンドタスクをGUIと同期させる。ワーカースレッドから呼び出されるため、backgroundWorker1_ProgressChanged(これは変更されたもの)を呼び出さないでください。 – Philippe

答えて

4

BackgroundWorkerのドキュメントでは、DoWorkメソッドを使用してUIオブジェクトを操作しないでください。また、UIオブジェクトの変更はReportProgressを通じて行う必要があります。私はリフレクターを見ていないが、おそらくあなたのために隠された "Invoke"を実行している。 PingCompletedイベントを発生させているものは、おそらく、ワーカースレッドまたはではなく、メインスレッド内で実行されています。

DoTaskがメインスレッドで実行されないVisual Studioデバッガのスレッドウィンドウに表示されます。ただし、ReportProgressが呼び出されると、ハンドラはメインスレッド上で実行されます。あなたのコントロールはおそらくメインスレッド上に作成されているため、例外は表示されません。 :あなたはDoWorkメソッド内で明示的にbackgroundWorker1_ProgressChangedをコールしようとすると今、そしてbackgroundWorker1_ProgressedChangedは、あなたのケースでは、PingCompletedイベントを発生させるのメソッドをDoWorkメソッドを実行するのと同じスレッドで実行、またはされます enter image description here

enter image description here

あなたはおそらくあなたのPingCompletedハンドラを呼び出すためにあなたの backgroundWorker1_ProgressChangedハンドラ、またはルート内InvokeRequiredチェックを追加することによって、このクロススレッドの例外を解決することができます

ReportProgress

EDIT:

PingCompletedハンドラからのReportProgressの呼び出しは元の送信者を失うため機能しません。

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged), sender, e); 
     return; 
    } 

    // The rest of your code goes here 
} 

EDIT 2応答:

private void PingUpdate (object sender, ProgressUpdated e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new Action<object, ProgressUpdated>(PingUpdate), sender, e); 
     return; 
    } 

    toolStripStatusLabel1.Text = e.GeneralStatus; 
    toolStripProgressBar2.Value = e.ProgressStatus; 
    toolStripStatusLable3.Text = e.SepcificStatus; 
} 
+0

ビジュアルのおかげで、私はそのような細部でスレッドを見ることができたことを知らなかった。 PingCompletedCallBackはReportProgressを呼び出すためにbackgroundWorkerにアクセスできますか? –

+0

ハンドラに2つの異なるプログレスバーがあり、送信者を使用してどのプログレスバーを更新するかを決定しました。あなたがルーティングのアプローチに従えば、あなたの '送付者'は常にバックグラウンドワーカーになります。ここには少なくとも2つのオプションがあります。 ping completedイベント用に個別のハンドラを作成し、その中のUIコンポーネントを(Invokeを使用して)更新します。これは、(送信者が...であるかどうか)チェックすれば厄介なことを取り除くでしょう。あなたが1つのハンドラを使用することに忠実であれば、進捗ハンドラ内にInvokeチェックを追加する必要があります。 – Tung

+0

私はアップデートコールを分離すると思います。更新イベントで呼び出しを実行する方が良いですか?編集2を参照してください –

関連する問題