C#中多个异步函数并发修改List<T>是否会引发并发问题?
多个异步函数并发修改List会导致并发问题吗?
会,绝对会出现并发问题。
List<T>是.NET中常用的集合类型,但它不是线程安全的——所有公共方法(包括Add)都没有内置同步机制。当多个异步任务(可能在不同线程执行)同时调用Add修改同一个List<T>实例时,会触发竞态条件,引发各种不可预期的问题:
- 元素被覆盖或丢失:两个线程同时执行
Add时,可能同时写入内部数组的同一位置 - 计数异常:
Count属性值与实际元素数量不符 - 抛出异常:比如
IndexOutOfRangeException,或集合内部结构损坏导致的其他错误
解决方案
针对你的业务场景,有几种可靠的处理方式:
1. 使用线程安全集合ConcurrentBag<T>
ConcurrentBag<T>是.NET专为多线程并发添加操作设计的线程安全集合,直接替换List<T>即可,无需额外同步代码:
var errors = new ConcurrentBag<Error>(); var awaitedTasks = new List<Task>(); awaitedTasks.Add(funcA(errors)); awaitedTasks.Add(funcB(errors)); awaitedTasks.Add(funcC(errors)); awaitedTasks.Add(funcD(errors)); await Task.WhenAll(awaitedTasks);
2. 手动添加锁同步
如果必须使用List<T>,可以在每个函数的Add操作外添加全局锁,确保同一时间只有一个线程修改集合:
// 全局锁对象,确保所有函数共用同一个锁 private static readonly object _errorListLock = new object(); // 示例异步函数 public async Task funcA(List<Error> errors) { // 执行业务逻辑 var error = await GetSomeError(); // 加锁后修改List lock (_errorListLock) { errors.Add(error); } }
3. 避免共享状态,合并结果(推荐)
让每个异步函数独立返回自己的错误列表,最后统一合并,完全规避并发修改问题:
// 修改异步函数为返回Task<List<Error>> var taskList = new List<Task<List<Error>>> { funcA(), funcB(), funcC(), funcD() }; // 等待所有任务完成,合并结果 var allErrors = await Task.WhenAll(taskList); var errors = allErrors.SelectMany(list => list).ToList();
这种方式不需要处理线程同步,逻辑更清晰,也没有锁带来的性能开销。
内容的提问来源于stack exchange,提问作者Anuj
相关产品推荐
相关产品推荐

