无法复现线程竞争效果,多线程代码执行结果有序问题咨询
问题分析与解决
你没观察到线程竞争的异常结果,不代表List<T>是线程安全的,只是当前执行条件没触发并发冲突而已,核心原因有两点:
List<T>.Add是轻量操作,1万次循环的执行时间极短,其中一个线程可能在另一个线程还没开始写入前就已经执行完毕,两个线程实际是串行执行的。- 系统CPU调度的随机性,如果当前CPU资源充足,线程会连续执行完所有循环,不会被打断切换,自然不会出现并发写入冲突。
要复现线程竞争问题,可以尝试以下修改:
- 大幅增加循环次数,比如改成100万次,让线程有足够的时间被调度切换。
- 在
list.Add(i)语句前加入Thread.Sleep(1),强制线程让出CPU,触发频繁的线程切换。
此时你会发现List<T>的最终元素数量可能小于20000(并发写入导致元素覆盖或计数错误),甚至直接抛出IndexOutOfRangeException(List内部扩容时的并发操作会破坏其结构)。
如果需要安全的多线程写入,有两种常用方案:
方案一:手动加锁
通过lock语句确保同一时间只有一个线程操作List:
static void Main(string[] args) { List<int> list = new List<int>(); object lockObj = new object(); // 专用锁对象 Thread t1 = new Thread(thread1); t1.IsBackground = true; t1.Start(); Thread t2 = new Thread(thread2); t2.IsBackground = true; t2.Start(); Thread.Sleep(3000); foreach(int i in list) { Console.WriteLine(i); } Console.ReadLine(); void thread1() { for (int i = 0; i < 10000; i++) { lock(lockObj) { list.Add(i); } } } void thread2() { for (int i = 10000; i < 20000; i++) { lock(lockObj) { list.Add(i); } } } }
方案二:使用线程安全集合
直接用.NET提供的ConcurrentBag<T>(或其他线程安全集合)替代List<T>:
static void Main(string[] args) { ConcurrentBag<int> list = new ConcurrentBag<int>(); Thread t1 = new Thread(thread1); t1.IsBackground = true; t1.Start(); Thread t2 = new Thread(thread2); t2.IsBackground = true; t2.Start(); Thread.Sleep(3000); // ConcurrentBag是无序集合,需排序后输出 foreach(int i in list.OrderBy(x => x)) { Console.WriteLine(i); } Console.ReadLine(); void thread1() { for (int i = 0; i < 10000; i++) { list.Add(i); } } void thread2() { for (int i = 10000; i < 20000; i++) { list.Add(i); } } }
内容的提问来源于stack exchange,提问作者Piggy Chu
相关产品推荐
相关产品推荐

