Individual await与Task.WhenAll执行效率无差异,我的理解有误吗?
问题分析:为什么并行执行存储过程没看到性能提升?
你有两个返回相同结果的方法,代码如下:
public static async Task<IEnumerable<RiskDetails>> ExecuteSqlStoredProcedureSelect<T>(IEnumerable<AccountInfo> linkedAccounts, string connectionString, string storedProcedure, int connTimeout = 10) { var responseList = new List<RiskDetails>(); using (IDbConnection conn = new SqlConnection(connectionString)) { foreach (var account in linkedAccounts) { var enumResults = await conn.QueryAsync<RiskDetails>(storedProcedure, new { UserID = account.UserID, CasinoID = account.CasinoID, GamingServerID = account.GamingServerID, AccountNo = account.AccountNumber, Group = account.GroupCode, EmailAddress = account.USEMAIL }, commandType: CommandType.StoredProcedure); if (enumResults != null) foreach (var response in enumResults) responseList.Add(response); } } return responseList; } public static async Task<IEnumerable<RiskDetails>> ExecuteSqlStoredProcedureSelectParallel<T>(IEnumerable<AccountInfo> linkedAccounts, string connectionString, string storedProcedure, int connTimeout = 10) { List<Task<IEnumerable<RiskDetails>>> tasks = new List<Task<IEnumerable<RiskDetails>>>(); var responseList = new List<RiskDetails>(); using (IDbConnection conn = new SqlConnection(connectionString)) { conn.Open(); foreach (var account in linkedAccounts) { var enumResults = conn.QueryAsync<RiskDetails>(storedProcedure, new { UserID = account.UserID, CasinoID = account.CasinoID, GamingServerID = account.GamingServerID, AccountNo = account.AccountNumber, Group = account.GroupCode, EmailAddress = account.USEMAIL }, commandType: CommandType.StoredProcedure, commandTimeout: 0); //add task tasks.Add(enumResults); } //await and get results var results = await Task.WhenAll(tasks); foreach (var value in results) foreach (var riskDetail in value) responseList.Add(riskDetail); } return responseList; }
你对两个方法的执行流程理解如下:
对ExecuteSqlStoredProcedureSelect的理解
- 执行账户#1的查询
- 等待查询#1的结果
- 接收查询#1的结果
- 执行账户#2的查询
- 等待查询#2的结果
- 依此类推
对ExecuteSqlStoredProcedureSelectParallel的理解
- 将所有任务添加至集合实例
- 调用
Task.WhenAll,开始执行所有账户#n的查询 - 查询相对并行地在SQL Server上执行
- 所有查询执行完成后,
Task.WhenAll返回
你的理解存在的关键错误
你对第一个方法的流程理解是完全正确的,它确实是串行执行每个账户的存储过程调用,完成一个再启动下一个。
但对第二个方法的理解有核心误区:你在同一个数据库连接上启动多个异步查询,这并不会真正并行执行。
SQL Server的单个连接是串行化处理请求的——即使你用异步方法提交多个查询,它们也会在同一个连接上排队执行,一个完成后才会处理下一个。也就是说,你的ExecuteSqlStoredProcedureSelectParallel方法看起来是并行提交任务,但实际上这些任务在数据库端还是串行执行的,和第一个方法没有本质区别,自然看不到性能提升。
怎么改才能真正实现并行?
要让存储过程调用真正并行执行,你需要为每个任务创建独立的数据库连接:
public static async Task<IEnumerable<RiskDetails>> ExecuteSqlStoredProcedureSelectParallel<T>(IEnumerable<AccountInfo> linkedAccounts, string connectionString, string storedProcedure, int connTimeout = 10) { var tasks = linkedAccounts.Select(async account => { using (var conn = new SqlConnection(connectionString)) { await conn.OpenAsync(); return await conn.QueryAsync<RiskDetails>(storedProcedure, new { UserID = account.UserID, CasinoID = account.CasinoID, GamingServerID = account.GamingServerID, AccountNo = account.AccountNumber, Group = account.GroupCode, EmailAddress = account.USEMAIL }, commandType: CommandType.StoredProcedure, commandTimeout: 0); } }); var results = await Task.WhenAll(tasks); return results.SelectMany(r => r); }
这样每个查询都使用独立的连接,SQL Server可以同时处理多个请求,才能真正体现并行的性能优势。
不过要注意:并行执行会增加数据库的负载,你需要根据数据库的承载能力控制并行度(比如用SemaphoreSlim限制并发连接数),避免压垮数据库。
内容的提问来源于stack exchange,提问作者monstertjie_za
相关产品推荐
相关产品推荐

