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