You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用Task.WaitAll相比多次await无性能提升?

问题

我原本以为用Task.WaitAll替代多次await是更优选择,查了相关讨论后也觉得前者能提升性能,但实际测试却发现多次await的总耗时更短——Task.WaitAll耗时1346ms,多次await耗时995ms。以下是我的测试代码和Method1实现,想知道这是什么原因?

Task.WaitAll版本测试代码

Stopwatch sw = Stopwatch.StartNew();
var t1 = Method1()
var t2 = Method2();
var t3 = Method3();
var t4 = Method4();
var t5 = Method5();
//note - in the real code there are like 10 await calls 
//which I changed to use like above code.

Task.WaitAll(t1,t2,t3,t4,t5);

for (int i = 0; i < 20; i++)
{
    someObject[i].prop1 = t1.Result[i];
    someObject[i].prop2 = t2.Result[i];
    someObject[i].prop3 = t3.Result[i];
    someObject[i].prop4 = t4.Result[i];
    someObject[i].prop5 = t5.Result[i];        
}

sw.Stop();
Debug.WriteLine($"Time taken using wait all concept {sw.ElapsedMilliseconds}");
// Time taken using wait all concept 1346 
// Time taken in **multiple** await is 995 ms

多次await版本测试代码

Stopwatch sw = Stopwatch.StartNew();

List<List<someObj1>> sth1 = await Method1()
List<List<someObj2>> sth2 = await Method1()
List<List<someObj3>> sth3 = await Method1()
List<List<someObj4>> sth4 = await Method1()
List<List<someObj5>> sth5 = await Method1()

for (int i = 0; i < claimData.Count; i++)
{
    someObject[i].prop1 = sth1[i];
    someObject[i].prop2 = sth2[i];
    someObject[i].prop3 = sth3[i];
    someObject[i].prop4 = sth4[i];
    someObject[i].prop5 = sth5[i];        
}

sw.Stop();
Debug.WriteLine($"Time taken in **multiple** await is {sw.ElapsedMilliseconds}");
// Time taken in **multiple** await is 995 ms

Method1实现

private async Task<List<someObject>> Method1()
{
    List<List<someObject>> something = new List<List<someObject>>();
    var storedProcCriteria = new
    {
        //set up the object with parameters.
    };

    using (var db = _sqlConn.GetDbConnection())
    {
        string sql = @"someStoredProcedure";
        SqlMapper.GridReader results = await db.QueryMultipleAsync(sql, storedProcCriteria, commandType: CommandType.StoredProcedure);

        while (!results.IsConsumed)
        {
            data = results.Read<someObject>().ToList();
            something.Add(data);
        }
        return something;
    }
}
原因分析
  • 数据库连接池限制:Method1每次执行都会从连接池获取数据库连接。用Task.WaitAll时,5个任务同时发起请求,一旦超过连接池能提供的空闲连接数,后续任务就会排队等待连接,直接拉长总耗时;而多次await是串行执行,每次仅占用一个连接,不会触发连接池排队,耗时反而更短。
  • Task.WaitAll的阻塞特性:Task.WaitAll是同步阻塞调用,会让当前线程处于等待状态,无法被线程池复用;而await会释放当前线程回到线程池,后续任务调度更高效。如果运行环境是UI线程或旧版ASP.NET,这种阻塞还可能引发线程饥饿,进一步影响性能。
  • 线程调度开销:多任务并行时,线程池需要调度多个线程处理请求,会产生额外的上下文切换开销;串行await复用线程池中的线程,上下文切换更少,开销更低。
  • 测试代码的细节差异:注意到多次await的代码里所有调用都是Method1,而Task.WaitAll版本调用了Method1到Method5。如果这些方法的实际逻辑(比如存储过程参数、执行时间)有差异,也可能影响结果,但从现象来看,连接池限制是最核心的原因。

内容的提问来源于stack exchange,提问作者Vivek Shukla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:05:35