正在等待C#中的后台线程
本文关键字:后台 线程 在等待 | 更新日期: 2023-09-27 18:22:16
之间有什么区别
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>
?但如果不是这样的话,编译器是否应该给出一个错误,说我们没有返回任何东西?
首先,应用程序被卡住的真正原因是死锁。这是因为您使用的是Task.Wait
,它正在阻塞等待UI线程完成的任务上的UI线程。
之所以会发生这种情况,是因为只有一个UI线程,所以有一个SynchronizationContext
只使用该特定线程。
您可以使用ConfigureAwait(false)
来修复死锁,它不使用SynchronizationContext
:
private static async Task<T> ExecuteInBackgroundThread<T>()
{
await Task.Run(() =>
{
// do some long running operation
}).ConfigureAwait(false);
}
除此之外,只要ExecuteInBackgroundThread
中不再有对Task.Run
调用之外的代码,这两个选项就基本相同。第一个选项稍微快一点,因为您使用的是task中的任务。直接运行。第二个选项在上面添加了另一个冗余异步层(包括状态机等)。
结论:你应该使用第一个选项。但是不要使用Wait
,它不仅会阻塞线程,而且在某些情况下可能会导致死锁。
让我们看看对每个版本调用Execute
的作用:
第一个版本调用CCD_ 12,CCD_。然后Execute
阻塞,直到返回的任务完成。
第二个版本调用ExecuteInBackgroundThread
,它创建一个Task
,并调度一个将在任务完成时执行的延续,并返回另一个任务。然后CCD_ 17阻塞直到返回的任务完成。
不同之处在于,由于await关键字,第二个版本安排了一个延续。它的效果取决于调用Execute
的线程的同步上下文。当您在UI线程上时,第二个版本将死锁,因为继续操作是在由于调用Wait
而被阻止的UI线程上安排的。
如果使用控制台应用程序,则不会出现死锁,因为默认同步上下文使用线程池来执行延续。