如何用async/await处理大量任务?Azure Function Dapper连接异常排查
嘿,我来帮你解决这两个问题哈!
1. 如何使用async/await处理潜在的大量任务?
当需要处理大量异步任务时,直接用Task.WhenAll可能会导致并发过高,耗尽系统资源(比如线程池、网络连接),这里有几个实用的处理方式:
控制并发数(最常用)
用SemaphoreSlim来限制同时运行的任务数量,避免一次性压垮系统。比如:var semaphore = new SemaphoreSlim(10); // 同时最多10个任务 var tasks = new List<Task>(); foreach (var item in largeTaskList) { await semaphore.WaitAsync(); tasks.Add(Task.Run(async () => { try { // 执行你的异步任务逻辑 await ProcessItemAsync(item); } finally { semaphore.Release(); } })); } await Task.WhenAll(tasks);分批处理任务
把大量任务拆分成小批次,每批完成后再处理下一批,适合不需要高并发的场景:int batchSize = 50; var batches = largeTaskList.Chunk(batchSize); // .NET 6+ 自带Chunk方法 foreach (var batch in batches) { var batchTasks = batch.Select(item => ProcessItemAsync(item)); await Task.WhenAll(batchTasks); // 可以在这里处理当前批次的结果,或者做一些清理 }流式处理(异步枚举)
如果不需要等待所有任务完成,而是可以逐个处理结果,用IAsyncEnumerable来流式执行,减少内存占用:async IAsyncEnumerable<Result> ProcessTasksAsync(IEnumerable<Item> items) { foreach (var item in items) { var result = await ProcessItemAsync(item); yield return result; } } // 使用方式 await foreach (var result in ProcessTasksAsync(largeTaskList)) { // 逐个处理结果 HandleResult(result); }
2. Azure Function中Dapper查询抛出连接关闭/断开异常的排查与解决
从你给出的代码片段和异常信息来看,这个问题大概率和连接的生命周期管理或者Azure环境下的网络/连接池问题有关,给你几个排查和解决方向:
首先检查最常见的问题:异步操作未正确await
你的代码里var t...是不是有未被await的异步操作?比如如果在using (var conn)块里启动了一个任务但没await,那么当using块结束时,连接会被自动释放,后续任务再用这个连接就会抛出异常。确保所有使用连接的异步操作都在using块内完成并被await:
using (var conn = new SqlConnection(_dbConnectionString)) { await conn.OpenAsync(); List<Guid> ids = await conn.QueryAsync<Guid>("SELECT Id From Table1;"); // 确保这里的所有后续异步操作都被await,比如: var details = await conn.QueryAsync<Detail>("SELECT * From Table2 Where Id IN @Ids;", new { Ids = ids }); // 不要让任何未await的任务逃出using块 }
优化数据库连接字符串配置
Azure环境下网络波动或连接池配置不合理也会导致这类问题,调整连接字符串的这些参数:
Server=tcp:your-server.database.windows.net,1433;Initial Catalog=your-db;Persist Security Info=False;User ID=your-user;Password=your-pass;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Max Pool Size=200;Keep Alive=30;
Max Pool Size:调整连接池最大数量(默认100),如果Function并发高可以适当调高Connection Timeout:延长连接超时时间,避免短时间内连接失败Keep Alive:保持连接活跃,防止Azure网络防火墙断开空闲连接
添加重试逻辑处理临时异常
偶尔的网络波动导致的连接异常可以通过重试解决,比如用简单的重试逻辑:
int retryCount = 3; for (int i = 0; i < retryCount; i++) { try { using (var conn = new SqlConnection(_dbConnectionString)) { await conn.OpenAsync(); List<Guid> ids = await conn.QueryAsync<Guid>("SELECT Id From Table1;"); // 后续逻辑 return ids; } } catch (InvalidOperationException ex) when (ex.Message.Contains("connection is closed") || ex.Message.Contains("connection has been broken")) { if (i == retryCount - 1) throw; // 最后一次重试失败就抛出 await Task.Delay(100 * (i + 1)); // 指数退避等待 } }
排查查询性能问题
如果查询本身执行时间过长,可能导致连接被数据库端主动断开,或者占用连接池资源过久。检查你的SQL查询是否有优化空间:
- 给
Table1的Id字段加索引(如果还没加) - 避免返回不必要的数据,只查询需要的字段
- 如果数据量很大,考虑分页查询而不是一次性查询所有Id
内容的提问来源于stack exchange,提问作者JuChom
相关产品推荐
相关产品推荐

