在任务正文中使用.Result和在异步方法中使用await有什么区别?

时间:2018-10-06 16:36:15

标签: c# multithreading asynchronous async-await task

我设法找到了在互联网上发布的类似问题的一些答案,但是没有人解释说它令人满意,足以使我理解下面代码的区别。

我知道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);
    }
}

2 个答案:

答案 0 :(得分:1)

ViewDidLoad将同步执行您的代码,即您无视.Result和它后面的整个Task的本质。就像TPL所说的那样,它是编译器以一种古老的“回调”方式(又称“ await”)重写标记的一种标记,这种方式是异步 >完全相同的计算方式。

更简单:与JavaScript相比,您应尽可能选择await

答案 1 :(得分:0)

由于第一个示例在线程池线程上启动新任务,因此在存在等效的同步方法时,无需调用异步方法。它没有任何好处,只是不必要地增加了管理异步任务所涉及的一些开销。只需使用.GetResponse()代替.GetResponseAsync().Result.ReadToEnd()代替.ReadToEndAsync().Result

  

await与.Result不同,它不会阻止调用线程

并非总是如此。如果等待的方法决定执行,则它们可以同步(部分或完全)执行。

  

例如,有什么区别吗?

两个示例之间有很多区别。尽管它们在某些情况下可能无关紧要,但在其他情况下却可能至关重要:

  1. AggregateException

    当任务中发生异常或任务被取消时,调用Task.Result会将异常包装在AggregateException中。相反,await执行此任务将引发原始异常。因此,catch特定例外时必须小心。

  2. 线程

    在第一个示例中,整个方法将在同一(线程池)线程上执行。第二个示例可以在多个不同的线程上执行,具体取决于当前的SynchronizationContext。应该避免对 thread-affinity 敏感的代码。

  3. SynchronizationContext

    第一个示例将在没有SynchronizationContext的情况下执行,而第二个示例将在每个SynchronizationContext之后恢复原始的await。对于控制台应用程序,这无关紧要。但是在WPF或WinForms应用程序中,只能从相应的同步上下文中访问UI元素。

  4. 异步执行

    在第一个示例中,PrintPageAsync将在新任务排队等待执行后立即返回,而第二个示例将同步执行直到第一个await(甚至可能在此之后)。这可能会对GUI响应产生严重影响,尤其是在异步方法使用WebRequest时,因为GetResponseAsync()方法同步执行DNS解析(请参见How to create HttpWebRequest without interrupting async/await?)。因此,当从UI线程调用使用Task.Run()的方法时,建议将代码包装在WebRequest中。