使用Task.WhenEach时向任务列表追加任务是否安全?相关实现疑问
问题解答
循环内新增的Task不会被Task.WhenEach捕获
Task.WhenEach在调用时会基于传入的任务集合生成一个固定的异步枚举器,它只会跟踪调用瞬间集合里的任务。后续你向tasks列表添加的新任务,不会被这个已经初始化的枚举器识别,自然不会被处理。
正确的实现方式(含限流)
你补充的基于SemaphoreSlim限流+Task.WhenAny循环的写法是完全可行的,能动态处理新增任务,逻辑清晰且能控制并发量。完整代码如下:
var tasks = new List<Task<List<int>>>(); var semaphore = new SemaphoreSlim(2); await semaphore.WaitAsync(); tasks.Add(MyTask(semaphore, 0)); while (tasks.Any()) { var completedTask = await Task.WhenAny(tasks); tasks.Remove(completedTask); var data = await completedTask; foreach (var i in data) { await semaphore.WaitAsync(); tasks.Add(MyTask(semaphore, i)); } } Console.WriteLine("Exiting"); async Task<List<int>> MyTask(SemaphoreSlim semaphoreSlim, int i) { await Task.Delay(3000); semaphoreSlim.Release(); return [i+1, i+2]; }
性能优化建议
如果需要处理大量任务,List<Task>的Remove操作是O(n)复杂度,可能影响性能。可以改用ConcurrentQueue<Task<List<int>>>存储任务,配合队列操作替代Remove,提升效率:
var taskQueue = new ConcurrentQueue<Task<List<int>>>(); var semaphore = new SemaphoreSlim(2); await semaphore.WaitAsync(); taskQueue.Enqueue(MyTask(semaphore, 0)); while (!taskQueue.IsEmpty) { var completedTask = await Task.WhenAny(taskQueue); // 从队列中移除已完成的任务,保留未完成的任务 var tempTasks = new List<Task<List<int>>>(); while (taskQueue.TryDequeue(out var task)) { if (task != completedTask) tempTasks.Add(task); } foreach (var task in tempTasks) { taskQueue.Enqueue(task); } var data = await completedTask; foreach (var i in data) { await semaphore.WaitAsync(); taskQueue.Enqueue(MyTask(semaphore, i)); } } Console.WriteLine("Exiting"); async Task<List<int>> MyTask(SemaphoreSlim semaphoreSlim, int i) { await Task.Delay(3000); semaphoreSlim.Release(); return [i+1, i+2]; }
内容的提问来源于stack exchange,提问作者schgab
相关产品推荐
相关产品推荐

