C# 什么';在任务体中使用.Result和在异步方法中使用await有什么区别?
我设法在网上找到了一些类似问题的答案,但没有一个解释得足够令人满意,足以让我理解下面代码中的区别 我知道wait和.Result不同,它不会阻止调用线程。但是,如果我们试图从一个任务访问这个属性,而这个任务无论如何都不会阻止它呢 例如,这两者之间有什么区别吗C# 什么';在任务体中使用.Result和在异步方法中使用await有什么区别?,c#,multithreading,asynchronous,async-await,task,C#,Multithreading,Asynchronous,Async Await,Task,我设法在网上找到了一些类似问题的答案,但没有一个解释得足够令人满意,足以让我理解下面代码中的区别 我知道wait和.Result不同,它不会阻止调用线程。但是,如果我们试图从一个任务访问这个属性,而这个任务无论如何都不会阻止它呢 例如,这两者之间有什么区别吗 public static Task PrintPageAsync(string url) { return Task.Run(() => { WebRequest webRequest = WebRe
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);
}
}
.Result
将同步执行您的代码,即您忽略了任务的本质以及整个TPL
的支持<代码>等待
,正如它所说的,是编译器以一种很好的旧的“回调”方式(a-ka典型的JavaScript
)重写方法的标记,这是一种异步方式来完成完全相同的计算
更简单:只要可能,您应该更喜欢
wait
而不是。Result
由于第一个示例在线程池线程上启动一个新任务,因此当存在等效的同步方法时,调用异步方法是不必要的。它没有任何好处,只是在管理异步任务时增加了一些不必要的开销。只需使用.GetResponse()
而不是.GetResponseAsync().Result
和.ReadToEnd()
而不是.ReadToEndAsync().Result
与.Result不同,wait不会阻止调用线程
这并不总是正确的。等待的方法可以(部分或完全)同步执行,如果它们决定这样做的话
例如,有什么区别吗
这两个例子有很多不同之处。虽然它们在某些情况下可能无关紧要,但在另一种情况下可能至关重要:
task.Result
将异常包装在aggregateeexception
中。相反,等待
等待此任务将抛出原始异常。因此,在捕获特定异常时必须小心
SynchronizationContext
。应避免使用对线程关联敏感的代码
PrintPageAsync
将在新任务排队等待执行后立即返回,而第二个将同步执行,直到第一个wait
(或者甚至可能在此之后)。这可能会对GUI responsivnes产生严重影响,尤其是当异步方法使用WebRequest
时,因为GetResponseAsync()
方法同步执行DNS解析(请参阅)。因此,当从UI线程调用使用WebRequest
的方法时,建议在Task.Run()中包装代码
没关系,区别仍然是它会阻塞。同样,将异步和阻塞混合在一起也会导致代码锁定。这很有趣,你所说的“它将阻塞”是什么意思?我在Main方法中使用它,如下所示:PrintPageAsync(@);while(true){Console.Write(“*”)}无论我使用哪一个,它都不会阻塞。它会立即开始打印星号,一段时间后它会打印页面,并且在这之后仍会打印星号。这是因为第一个是在另一个线程中启动的。在这种情况下,您不妨调用同步的
ReadToEnd
。删除任务。运行,看看会发生什么pens。我想我理解,但我的重点是,当我使用Task.Run时,两者之间有什么区别,所以它从另一个线程开始。调用它时,它的行为会与异步方法不同吗?最终的区别是使用了多少线程。对于这个简单的示例,差别不大,但如果这是在se上编写的代码web服务调用背后的服务器,那么当您收到许多请求时,服务器会因为一堆阻塞的线程而变慢。这有什么关系呢?如果我们在任务体中使用它,它不是只在任务体中阻塞,并且调用方法不受影响吗?无论我使用哪个方法,它们都会给我相同的结果。例如,PrintPageAsync(@”);虽然(true){Console.Write(“*”;}在Main方法中使用此选项会打印星号,在一段时间后打印页面内容并再次打印星号。我认为您必须深入了解异步性是什么。它会一直阻塞,直到您得到底层计算的结果,或者异常情况下被取消/失败。任何一种方式都意味着同步执行,这与任务的目的相反。我理解其中的区别,但我认为,因为我们在另一个线程中启动任务,所以它不会阻止调用线程,当我们使用阻止时。结果,我们阻止了任务的线程,而不是调用线程。我完全错了吗?另外,正如我所提到的,这两种方法的输出(使用我前面评论中的代码时)是相同的。如果其中一个阻塞,而另一个不阻塞,那么为什么输出是相同的?@pink.Overload您的示例只是误用了任务。除非有绝对必要,否则不要将任务
包装到任务
中。这样的代码在线程队列消耗方面效率非常低