我设法找到了在互联网上发布的类似问题的一些答案,但是没有人解释说它令人满意,足以使我理解下面代码的区别。
我知道await与.Result不同,它不会阻塞调用线程。但是,如果我们尝试从任务中访问此属性,而该操作仍然不会阻止它,该怎么办?
例如,两者之间有什么区别
public static Task PrintPageAsync(string url)
{
return Task.Run(() =>
{
WebRequest webRequest = WebRequest.Create(url);
WebResponse response = webRequest.GetResponseAsync().Result;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string text = reader.ReadToEndAsync().Result;
Console.WriteLine(text);
}
});
}
还有这个
public static async Task PrintPageAsync(string url)
{
WebRequest webRequest = WebRequest.Create(url);
WebResponse response = await webRequest.GetResponseAsync();
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string text = await reader.ReadToEndAsync();
Console.WriteLine(text);
}
}
答案 0 :(得分:1)
ViewDidLoad
将同步执行您的代码,即您无视.Result
和它后面的整个Task
的本质。就像TPL
所说的那样,它是编译器以一种古老的“回调”方式(又称“ await
”)重写标记的一种标记,这种方式是异步 >完全相同的计算方式。
更简单:与JavaScript
相比,您应尽可能选择await
。
答案 1 :(得分:0)
由于第一个示例在线程池线程上启动新任务,因此在存在等效的同步方法时,无需调用异步方法。它没有任何好处,只是不必要地增加了管理异步任务所涉及的一些开销。只需使用.GetResponse()
代替.GetResponseAsync().Result
和.ReadToEnd()
代替.ReadToEndAsync().Result
。
await与.Result不同,它不会阻止调用线程
并非总是如此。如果等待的方法决定执行,则它们可以同步(部分或完全)执行。
例如,有什么区别吗?
两个示例之间有很多区别。尽管它们在某些情况下可能无关紧要,但在其他情况下却可能至关重要:
AggregateException
当任务中发生异常或任务被取消时,调用Task.Result
会将异常包装在AggregateException
中。相反,await
执行此任务将引发原始异常。因此,catch
特定例外时必须小心。
线程
在第一个示例中,整个方法将在同一(线程池)线程上执行。第二个示例可以在多个不同的线程上执行,具体取决于当前的SynchronizationContext
。应该避免对 thread-affinity 敏感的代码。
SynchronizationContext
第一个示例将在没有SynchronizationContext
的情况下执行,而第二个示例将在每个SynchronizationContext
之后恢复原始的await
。对于控制台应用程序,这无关紧要。但是在WPF或WinForms应用程序中,只能从相应的同步上下文中访问UI元素。
异步执行
在第一个示例中,PrintPageAsync
将在新任务排队等待执行后立即返回,而第二个示例将同步执行直到第一个await
(甚至可能在此之后)。这可能会对GUI响应产生严重影响,尤其是在异步方法使用WebRequest
时,因为GetResponseAsync()
方法同步执行DNS解析(请参见How to create HttpWebRequest without interrupting async/await?)。因此,当从UI线程调用使用Task.Run()
的方法时,建议将代码包装在WebRequest
中。