让我们看一下以下两个示例:
public class MyClass
{
public async Task Main()
{
var result1 = "";
var result2 = "";
var request1 = await DelayMe();
var request2 = await DelayMe();
result1 = request1;
result2 = request2;
}
private static async Task<String> DelayMe()
{
await Task.Delay(2000);
return "";
}
}
并且:
public class MyClass
{
public async Task Main()
{
var result1 = "";
var result2 = "";
var request1 = DelayMe();
var request2 = DelayMe();
result1 = await request1;
result2 = await request2;
}
private static async Task<String> DelayMe()
{
await Task.Delay(2000);
return "";
}
}
在第一个示例中,您通常会编写async await
代码,其中一件事情发生在另一件事情之后并正确等待。
第二个方法是首先调用async Task
方法,但稍后会await
对其进行调用。
第一个示例花费了4000ms
一点点时间来执行,因为await
在计算第二个请求之前正在计算第一个请求;但是第二个示例花费了2000ms
一点点。发生这种情况是因为Task
实际上是在执行跨过var request1 = DelayMe();
行后立即开始运行的,这意味着request1
和request2
是并行运行的。此时,看来await
关键字只是确保了Task
的计算。
第二种方法的感觉和作用类似于await Task.WhenAll(request1, request2)
,但是在这种情况下,如果2个请求中的某些操作失败,您将立即获得异常,而不是等待所有计算然后得到{{1 }}。
我的问题是,当第二种方法的结果不取决于执行时,使用第二种方法并行运行多个AggregateException
个await
是否有缺点(性能)?其他的?查看降低的代码,看起来第二个示例生成了相等数量的Task
1 System.Threading.Tasks.Task
async await状态机流?
答案 0 :(得分:3)
如果2个请求中的某些操作失败,您将立即获得一个异常,而不是等待所有计算之后再获得AggregateException。
如果在 first 请求中某项失败,则为是。如果 second 请求失败,则不会,直到await
完成该任务后,您才可以检查第二个请求的结果。
我的问题是,当一种方法的结果不依赖于另一种方法的执行时,使用第二种方法并行运行多个可等待任务是否有缺点(性能或其他方面)?查看降低的代码,看起来第二个示例生成了相等数量的System.Threading.Tasks.Task1per等待项目,而第一个则没有。这还在通过异步等待状态机流吗?
它仍在通过状态机流程。我倾向于推荐await Task.WhenAll
,因为代码的意图更加明确,但是有些人不喜欢“即使有异常也总是等待”的行为。另一面是Task.WhenAll
始终收集所有异常-如果您具有快速失败的行为,则可以忽略某些异常。
关于性能,并发执行会更好,因为您可以并发执行多个操作。因为async
/ await
不会使用其他线程,所以没有线程池耗尽的危险。
作为一个旁注,我建议对此使用“异步并发”而不是“并行”,因为对于许多人来说,“并行”意味着并行处理,即Parallel
或PLINQ,这将是在这种情况下使用错误的技术。
答案 1 :(得分:-1)
使用第二种方法并行运行多个等待的任务的缺点是并行性不明显。而且明显的并行性(换句话说是隐式多线程)并不危险,因为众所周知,可能引入的错误是不一致的,并且偶尔会被观察到。让我们假设在生产环境中运行的实际DelayMe
是唯一的波纹管:
private static int delaysCount = 0;
private static async Task<String> DelayMe()
{
await Task.Delay(2000);
return (++delaysCount).ToString();
}
依次等待的对DelayMe
的呼叫将返回递增的数字。并行等待的呼叫偶尔会返回相同的号码。