为什么不调用Task .Result死锁?
几个月前阅读这篇文章之后,我变得很擅长获取Task
的Result
,并且通过ConfigureAwait(false)
或Task.Run
不断地将所有调用包裹Task.Run
。 但是,由于某种原因,以下代码成功完成:
public static void Main(string[] args) { var arrays = DownloadMany(); foreach (var array in arrays); } IEnumerable DownloadMany() { string[] links = { "http://google.com", "http://microsoft.com", "http://apple.com" }; using (var client = new HttpClient()) { foreach (var uri in links) { Debug.WriteLine("Still here!"); yield return client.GetByteArrayAsync(uri).Result; // Why doesn't this deadlock? } } }
代码打印Still here!
3次然后退出。 这是否特定于HttpClient
,可以安全地调用Result
(如编写它的人用ConfigureAwait(false)
填充它)?
Task.Result
只会在某些SynchronizationContext
存在时阻塞。 在控制台应用程序中,没有一个在ThreadPool
上安排继续。 就像使用ConfigureAwait(false)
。
例如,在UI线程中,有一个调度单个UI线程的延续。 如果使用UI线程与Task.Result
同步等待, Task.Result
在UI线程上完成的任务就会出现死锁。
此外,死锁取决于GetByteArrayAsync
的实现。 如果它是异步方法并且它等待不使用ConfigureAwait(false)
则只能死锁。
如果您愿意,可以使用Stephen Cleary的AsyncContext
,它将适当的SynchronizationContext
添加到您的控制台应用程序,以测试您的代码是否可以在UI应用程序(或ASP.Net)中阻止。
关于HttpClient
(以及大多数.NET)的任务返回方法:它们在技术上并不是异步的。 他们不使用async
和await
关键字。 他们只是返回一个任务。 通常是Task.Factory.FromAsync
的包装器。 所以无论如何它可能“安全”阻挡它们。