我正在审查一些代码并试图提出一个技术原因,为什么您应该或不应该使用Task.WhenAll(Tasks[])并行进行 Http 调用。Http 调用调用不同的微服务,我猜其中一个调用可能需要也可能不需要一些时间来执行......(我想我对此并不感兴趣)。我正在使用 BenchmarkDotNet 来让我了解是否消耗了更多内存,或者执行时间是否有很大不同。以下是基准的过度简化示例:
[Benchmark]
public async Task<string> Task_WhenAll_Benchmark()
{
var t1 = Task1();
var t2 = Task2();
await Task.WhenAll(t1, t2);
return $"{t1.Result}===={t2.Result}";
}
[Benchmark]
public async Task<string> Task_KeepItSimple_Benchmark()
{
return $"{await Task1()}===={await Task2()}";
}
Run Code Online (Sandbox Code Playgroud)
Task1和Task2方法真的很简单(我HttpClient在类中有一个静态)
public async Task<string> Task1()
{
using (var request = await httpClient.GetAsync("http://localhost:8000/1.txt"))
{
return $"task{await request.Content.ReadAsStringAsync()}";
}
}
public async Task<string> Task2()
{
using (var request = await httpClient.GetAsync("http://localhost:8000/2.txt"))
{
return $"task{await request.Content.ReadAsStringAsync()}";
}
}
Run Code Online (Sandbox Code Playgroud)
还有我的结果
| 方法 | 意思 | 错误 | 标准差 | 第 0 代 | 第一代 | 第 2 代 | 已分配 |
|---|---|---|---|---|---|---|---|
| Task_WhenAll_Benchmark | 1.138 毫秒 | 0.0561 毫秒 | 0.1601 毫秒 | —— | —— | —— | 64 KB |
| Task_KeepItSimple_Benchmark | 1.461 毫秒 | 0.0822 毫秒 | 0.2331 毫秒 | —— | —— | —— | 64 KB |
如您所见,内存不是问题,执行时间也不长。
我的问题真的是,是否有技术原因为什么你应该或不使用 Task.WhenAll()?这只是一种偏好吗?
我从 .net 核心团队的一个人那里看到了Async Guidance,但它并没有真正涵盖这种情况。
编辑:这是 .net 框架(4.6.1)而不是核心!
编辑 2:按照下面评论中的建议更新基准之一。
我更新了 KeepItSimple 方法的基准测试...
[Benchmark]
public async Task<string> Task_KeepItSimple_Benchmark()
{
var t1 = Task1();
var t2 = Task2();
return $"{await t1}===={await t2}";
}
Run Code Online (Sandbox Code Playgroud)
并有以下结果:
| 方法 | 意思 | 错误 | 标准差 | 第 0 代 | 第一代 | 第 2 代 | 已分配 |
|---|---|---|---|---|---|---|---|
| Task_WhenAll_Benchmark | 1.134 毫秒 | 0.0566 毫秒 | 0.1613 毫秒 | —— | —— | —— | 64 KB |
| Task_KeepItSimple_Benchmark | 1.081 毫秒 | 0.0377 毫秒 | 0.1070 毫秒 | —— | —— | —— | 64 KB |
现在我更加困惑 - 执行速度如何(尽管数量很少!)?我认为当你await得到结果时开始执行代码......
我的问题真的是,是否有技术原因为什么你应该或不使用 Task.WhenAll()?
在两个调用都失败的异常情况下,行为略有不同。如果await一次修改一次,则永远不会观察到第二次失败;第一次失败的异常会立即传播。如果使用Task.WhenAll,则观察到两种故障;两个任务都失败后会传播一个异常。
这只是一种偏好吗?
这主要只是偏好。我倾向于更喜欢,WhenAll因为代码更明确,但我一次await输入一个没有问题。