0

BackgroundWorker を使用して定期的に ping 操作を実行するツールに取り組んでいます。BackgroundWorker ProgressChanged イベントで問題が発生しています。ProgressChanged イベントのコードは次のとおりです。

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 と、ping が終了したときに pingcompletedcallback イベントの両方で呼び出されます。ProgressChanged イベントが PingCompletedCallback イベントから実行される場合にのみ、クロス スレッドの問題が発生します。2 番目の進行状況バーを更新しようとすると、エラーがスローされます。

一方の通話で発生しているのに、もう一方の通話では発生していない理由がわかりません。

PingCompletedCallBack は BackgroundWorker スレッドで発生していますか?それがクロススレッドの問題を引き起こしている理由ですか?

もしそうなら、バックグラウンドワーカーではなく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:

変更された ProgrssChanged イベント

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

次に、2 つ目の更新イベントを作成しました

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

私が残した唯一のことは、UI スレッドで実行されるような方法で PingCompletedCallback から新しいイベントを呼び出すことです。これは Invoke ステートメントが使用される場所ですか、それとも新しいイベントで Invoke を使用する必要がありますか?

4

1 に答える 1

4

BackgroundWorkerのドキュメントには、DoWork メソッドを使用して UI オブジェクトを操作するべきではなく、UI オブジェクトへの変更は ReportProgress を介して行う必要があると記載されています。リフレクターは見ていませんが、おそらく隠れた「呼び出し」を実行しています。イベントを発生させているものは、おそらくワーカースレッドまたはメインスレッドではないPingCompleted他のスレッド内で実行されています。

DoTaskメイン スレッドで実行されないVisual Studio デバッガーのスレッド ウィンドウに表示されます。ただし、ReportProgressが呼び出されると、ハンドラーはメイン スレッドで実行されます。コントロールはおそらくメイン スレッドで作成されているため、例外は表示されません。 ここに画像の説明を入力

backgroundWorker1_ProgressChangedここで、DoWork メソッド内で明示的に呼び出そうとすると、DoWork メソッドbackgroundWorker1_ProgressedChangedを実行している同じスレッド、またはあなたの場合はPingCompletedイベント を発生させているメソッドで実行されます。ここに画像の説明を入力

ハンドラー内に InvokeRequired チェックを追加するかbackgroundWorker1_ProgressChanged、PingCompleted ハンドラーを呼び出してルーティングすることで、このクロス スレッド例外を解決できる可能性があります。ReportProgress

編集:

ReportProgress元の送信者が失われるため、ハンドラーからの呼び出しは機能しPingCompletedません。

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
}

編集 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;
}
于 2012-03-08T06:25:43.440 に答える