web-dev-qa-db-ja.com

タスクからメインスレッドにコールバックする

スレッドについてよく知らないので、質問があります。バックグラウンドで何かをしたいのですが、バックグラウンドメソッドで特定の条件でメインスレッドに切り替えたいのですが、それ以外の場合はバックグラウンドで動作します。この機能を実現するにはどうすればよいですか? UIクラス(c#)からStartSyncThreadの呼び出しを使用しています

async void StartSyncThread()
{
    await DoSyncAsync();
}

Task DoSyncAsync()
{
    return Task.Run(() => DoSync());            
}

doSyncメソッドでは、UIを変更できるように、メインスレッドに切り替えたいと思います。これを行うための簡単な解決策を教えてください。前もって感謝します!

13
Jatin

最初に非同期プロセスを開始し、次にDispatcher.BeginInvokeを呼び出してUIスレッドに戻ります。

Task.StartNew(() =>
{
   // Do Something Async

   Dispatcher.BeginInvoke(() =>
   {
      // Update Your UI Here 
   });
});

Dispatcherは静的ではないことに注意してください。これは、ページオブジェクトのメソッドのように、コードがUIオブジェクトのメンバー関数の一部であることに依存しています。

10
Deeko

いくつかのアプローチがあります。

1つは、同期方式をさまざまな部分に分割することです。これは、同期メソッドがUIのさまざまな部分に入るさまざまなタイプのものを計算する場合に最適です。

async Task DoSyncAsync()
{
  myDataBoundUIProperty1 = await Task.Run(() => DoSync1());
  myDataBoundUIProperty2 = await Task.Run(() => DoSync2());
}

2つ目は、進捗レポートを使用することです。これは、UIの更新がすべて同じタイプである場合に最適です。

Task DoSyncAsync()
{
  Progress<MyProgressType> progress = new Progress<MyProgressType>(progressUpdate =>
  {
    myDataBoundUIProperty = progressUpdate;
  });
  return Task.Run(() => DoSync(progress));
}
void DoSync(IProgress<MyProgressType> progress)
{
  ...
  if (progress != null)
    progress.Report(new MyProgressType(...));
  ...
}

最終的な代替案がありますが、上記の2つのうちの1つを強くお勧めします。上記の2つのソリューションにより、コード設計が改善されます(関心の分離)。 3番目の方法は、UIコンテキストで任意のコードを実行するために使用できるTaskFactoryを渡すことです。

Task DoSyncAsync()
{
  var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
  var factory = new TaskFactory(scheduler);
  return Task.Run(() => DoSync(factory));
}
void DoSync(TaskFactory factory)
{
  ...
  scheduler.StartNew(() => { ... });
  ...
}

繰り返しになりますが、この最後の解決策は、UI更新ロジックをバックグラウンドタスクロジックと統合するため、お勧めしません。ただし、DispatcherまたはControlを直接使用するよりも優れています。

9
Stephen Cleary