由于我不了解线程,所以我有一个问题。 我想在背景和背景方法中做一些事情我想在某些条件下切换回主线程,否则在后台工作。 我该如何实现这一功能?我正在使用UI类(c#)
中的StartSyncThread调用async void StartSyncThread()
{
await DoSyncAsync();
}
Task DoSyncAsync()
{
return Task.Run(() => DoSync());
}
在DoSync方法中,我想切换回主线程,以便我可以更改UI。 请给我一个简单的解决方案来做到这一点。提前谢谢!
答案 0 :(得分:9)
有几种方法。
首先是将同步方法分成不同的部分。如果您的同步方法计算进入UI不同部分的不同类型的事物,那么这是最好的:
async Task DoSyncAsync()
{
myDataBoundUIProperty1 = await Task.Run(() => DoSync1());
myDataBoundUIProperty2 = await Task.Run(() => DoSync2());
}
第二种是使用进度报告。如果您的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(...));
...
}
还有最后的选择,但我强烈推荐上述两种中的一种。上述两种解决方案将带来更好的代码设计(关注点分离)。第三种方法是传入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更新逻辑与您的后台任务逻辑混淆。但它比直接使用Dispatcher
或Control
更好。
答案 1 :(得分:9)
首先启动异步过程,然后调用Dispatcher.BeginInvoke以返回UI线程。
Task.StartNew(() =>
{
// Do Something Async
Dispatcher.BeginInvoke(() =>
{
// Update Your UI Here
});
});
请注意,Dispatcher不是静态的 - 这依赖于您的代码是UI对象的成员函数的一部分,就像页面对象上的方法一样。