从任务返回到主线程的回调

16

我对线程不是很了解,有一个问题想请教。 我想在后台执行某些操作,并且在后台方法中,在满足某些条件的情况下切换回主线程,否则继续在后台工作。 我该如何实现这个功能?我正���使用从 UI 类(c#) 调用 StartSyncThread 的方式。

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

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

在DoSync方法中,我想切换回主线程以便可以更改UI。请给我一个简单的解决方案。提前感谢!


请查看 Control.Invoke(() 或者这个线程:https://dev59.com/fm855IYBdhLWcg3wc0Dy - Matthew Watson
2个回答

12

首先启动您的异步过程,然后调用Dispatcher.BeginInvoke以返回到UI线程。

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

   Dispatcher.BeginInvoke(() =>
   {
      // Update Your UI Here 
   });
});
请注意,Dispatcher不是静态的 - 这取决于您的代码作为UI对象的成员函数的一部分,例如在页面对象上的方法。

9

有几种方法。

第一种是将同步方法拆分为不同的部分。如果您的同步方法计算的不同类型的内容适用于UI的不同部分,则此方法最佳:

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

第二种方法是使用进度报告。如果您的界面更新都是相同类型的,则这种方法最好。
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(...));
  ...
}

有一种最终的选择,但我强烈建议使用上述两种方法之一。以上两种解决方案将导致更好的代码设计(责任分离)。第三种选择是传递一个TaskFactory,可用于在UI上下文中运行任意代码:

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

再次强调,我不建议使用这种最后的解决方案,因为它将UI更新逻辑与后台任务逻辑混淆在一起。但这比直接使用DispatcherControl要好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接