有什么区别:
public class Worker
{
public static void Execute()
{
ExecuteInBackgroundThread().Wait();
}
private static Task ExecuteInBackgroundThread()
{
return Task.Run(() =>
{
// do some long running operation
});
}
}
和
public class Worker
{
public static void Execute()
{
ExecuteInBackgroundThread().Wait();
}
private static async Task ExecuteInBackgroundThread()
{
await Task.Run(() =>
{
// do some long running operation
});
}
}
我注意到从我的Windows Phone应用程序的UI线程调用Worker.Execute()
的第二个版本被卡住了。
相反,使用第一个版本似乎一切正常。
ExecuteInBackgroundThread
的第二个版本实际上是否可以等待Task<T>
?但如果情况并非如此,编译器是否应该提出错误说我们没有返回任何内容?
答案 0 :(得分:5)
首先,你的应用程序卡住的真正原因是你有一个死锁。那是因为你正在使用Task.Wait
阻塞正在等待UI线程完成的任务的UI线程。
这是因为只有一个UI线程,所以只有SynchronizationContext
只使用该特定线程。
您可以使用不使用ConfigureAwait(false)
的{{1}}解决僵局:
SynchronizationContext
除此之外,两个选项几乎相同,只要private static async Task<T> ExecuteInBackgroundThread<T>()
{
await Task.Run(() =>
{
// do some long running operation
}).ConfigureAwait(false);
}
中没有更多代码在ExecuteInBackgroundThread
的调用之外。第一个选项稍快,因为您直接使用Task.Run中的任务。第二个选项在顶部添加另一个冗余异步层(包括状态机等)。
结论:您应该使用第一个选项。但是不要使用Task.Run
,它不仅会阻塞你的线程,而且在某些情况下可能会导致死锁。
答案 1 :(得分:2)
让我们看看每个版本调用Execute
的内容:
第一个版本调用ExecuteInBackgroundThread
,它会立即返回Task
。然后Execute
阻塞,直到返回的任务完成。
第二个版本调用ExecuteInBackgroundThread
创建一个Task
并安排一个延续,当任务完成时将执行该延续,并返回另一个任务。然后Execute
阻塞,直到返回的任务完成。
不同之处在于,由于await关键字,第二个版本具有连续调度。它的效果取决于您调用Execute
的线程的同步上下文。当您在UI线程上时,第二个版本将死锁,因为延迟是在UI线程上安排的,该线程因调用Wait
而被阻止。
如果您正在使用控制台应用程序,则不会出现死锁,因为默认同步上下文使用线程池来执行延续。