7

backgroundWorkerの要点は、時間のかかるタスクの後にUIを更新することです。コンポーネントは、WPFアプリでアドバタイズされたとおりに機能します。

ただし、私のテストでは、コールバックは呼び出し元のスレッドで呼び出されません。

[Test]
public void TestCallbackIsInvokedOnClientThread()
{

     var clientId = Thread.CurrentThread.ManagedThreadId;
     int callbackThreadId = -1;
     var manualEvent = new ManualResetEventSlim(false);

     var someUIControl = new TextBox();
     var bw = new BackgroundWorker();

     bw.DoWork += (s,e) => e.Result = 5 ; // worker thread

     bw.RunWorkerCompleted += (s, e) =>
                                  {
                                      try
                                      {
                                          callbackThreadId = Thread.CurrentThread.ManagedThreadId;
                                          //someUIControl.Text = callbackThreadId.ToString();
                                          manualEvent.Set();
                                      }
                                      catch (System.Exception ex)
                                      {
                                          Console.Out.WriteLine(ex.ToString());
                                      }
                                  };
     bw.RunWorkerAsync();

     if (!manualEvent.Wait(5000))
         Assert.Fail("no callback");
     Assert.AreEqual(clientId, callbackThreadId);
 }

結果メッセージ:Assert.AreEqualが失敗しました。予想:<15>。実際:<10>。クライアントスレッドでコールバックが呼び出されない

私は何が欠けていますか?

ユニットテストでは、次のような動作が見られます

------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9

Wpfアプリでは、これは

MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1

更新: ジャスティンの答えで、次の変更を加え、テストに合格しました

  • BackgroundWorkerを作成する前に SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));
  • スレッド間のシグナリングにイベントを使用する代わりに、メッセージポンプをシミュレートします

for (int i = 0; i < 3; i++)
{
    control.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
    Thread.Sleep(50);
}
4

4 に答える 4

10

実行しているコンテキストが異なるため、動作も異なります。

bw.RunWorkerAsync()を呼び出すと、SynchronizationContextがキャプチャされます。これは、RunWorkerCompleted呼び出しをディスパッチするために使用されます。

WPFでは、DispatcherSynchronizationContextを使用して、完了したコールバックをUIスレッドにマーシャリングします。テストでは、このマーシャリングは不要であるため、バックグラウンドワーカースレッドに残ります。

于 2012-11-23T11:20:26.893 に答える
3

バックグラウンドワーカーがコールバックを投稿できるように、呼び出しスレッドはメッセージポンピング(つまり、STAアパートメントであり、関連するディスパッチャーを持つ)をサポートする必要があると私は信じています。そうでない場合、バックグラウンドワーカーにはオプションがなく、独自のスレッドでコールバックを実行します。テストする場合は、このリンクを参照してください。

于 2012-11-23T11:17:25.850 に答える
0

ユーザーがウィンドウを閉じると保存が発生し、BackgroundWorkerを使用してホームウィンドウが更新され、ウィンドウを閉じたときにBackgroundWorkerを開始したスレッドが終了したため、RunWorkerCompletedが実行されないというコードの問題が発生しました。

バックグラウンドワーカーが完了した後、戻るスレッドができるように、ホームウィンドウのコンテキストで閉じるウィンドウの保存実行を変更する必要がありました。

于 2013-01-23T22:28:05.887 に答える
0

私の場合、Windowsフォームを使用しており、コントロールにはDispatcherプロパティがありません(dispatcherの定義なしの回答を参照してください)。

コントロール内のソリューションの代わりにDispatcher.CurrentDispatcherを使用すると、Gishuのソリューションも機能します。

テストの初期化について:

// I am using a field Dispatcher _dispatcher
_dispatcher = Dispatcher.CurrentDispatcher; 

そして、バックグラウンドタスクが完了するのを待つとき:

_dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Thread.Sleep(50);
于 2020-03-06T10:59:23.507 に答える