从任务中回调主线程

Jat*_*tin 13 c# windows-phone windows-phone-8

由于我不了解线程,所以我有一个问题.我想在背景和背景方法中做一些事情我想在某些条件下切换回主线程,否则在后台工作.我该如何实现这一功能?我正在使用UI类中的StartSyncThread调用(c#)

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

Task DoSyncAsync()
{
    return Task.Run(() => DoSync());            
}
Run Code Online (Sandbox Code Playgroud)

在DoSync方法中,我想切换回主线程,以便我可以更改UI.请给我一个简单的解决方案来做到这一点.提前致谢!

Ste*_*ary 9

有几种方法.

第一种是将同步方法分成不同的部分.如果您的同步方法计算进入UI不同部分的不同类型的事物,这是最好的:

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

第二是使用进度报告.如果您的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(...));
  ...
}
Run Code Online (Sandbox Code Playgroud)

有一个最后的选择,但我强烈推荐上述两个中的一个.上述两种解决方案将带来更好的代码设计(关注点分离).第三种方法是传入一个TaskFactory可用于在UI上下文中运行任意代码的方法:

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

同样,我不建议这个最后的解决方案,因为它将您的UI更新逻辑与您的后台任务逻辑混淆.但它比使用DispatcherControl直接更好.


Dee*_*eko 9

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

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

   Dispatcher.BeginInvoke(() =>
   {
      // Update Your UI Here 
   });
});
Run Code Online (Sandbox Code Playgroud)

请注意,Dispatcher不是静态的 - 这依赖于您的代码是UI对象的成员函数的一部分,就像页面对象上的方法一样.