如何在Parallel.ForEach中实现共享互锁变量?
在Parallel.ForEach中安全共享计数变量的实现方法
在并行循环中直接共享普通变量会引发竞态条件(多个线程同时读写导致计数不准确),必须用线程安全的机制来处理。以下是几种可靠的实现方式:
1. 使用Interlocked类(轻量级推荐)
Interlocked类提供原子操作,能确保计数的增减在多线程下不会被打断,性能比锁更优。
修改你的代码如下:
List<string> bob = new List<string>(); bob.Add("hey"); bob.Add("asdasf"); bob.Add("dfghfghd"); bob.Add("rtertdf"); bob.Add("2535dfgd"); bob.Add("sdfsdfzcxv"); bob.Add("sfgsdgsdfh"); bob.Add("23454567"); bob.Add("fgjuoiyhji"); bob.Add("ghjnbvdfg"); bob.Add("fghdtu5645"); bob.Add("565yhfhgh"); bob.Add("ewqrwy77684"); bob.Add("nbndrthw2"); int currentCount = 0; Parallel.ForEach(bob, peer => { // 原子递增并获取最新值 int count = Interlocked.Increment(ref currentCount); Console.WriteLine(peer + " : " + count); });
2. 使用lock语句(适合复杂操作)
如果需要对共享变量执行更复杂的逻辑(不止简单递增),可以用lock确保同一时间只有一个线程访问变量:
List<string> bob = new List<string>(); bob.Add("hey"); bob.Add("asdasf"); // 省略其他Add操作 int currentCount = 0; object lockObj = new object(); // 锁对象必须是引用类型 Parallel.ForEach(bob, peer => { lock (lockObj) { currentCount++; Console.WriteLine(peer + " : " + currentCount); } });
3. 使用Parallel.ForEach的本地状态重载(减少竞争)
当迭代次数非常多时,让每个线程维护自己的本地计数,最后合并到全局变量,能大幅减少线程间的竞争:
List<string> bob = new List<string>(); bob.Add("hey"); bob.Add("asdasf"); // 省略其他Add操作 int totalCount = 0; Parallel.ForEach(bob, // 初始化每个线程的本地状态 () => 0, // 每个迭代执行的逻辑,更新本地计数 (peer, state, localCount) => { localCount++; Console.WriteLine(peer + " : " + localCount); // 此处为线程本地计数,若需全局计数可在合并后输出 return localCount; }, // 每个线程结束后,合并本地计数到全局变量 localCount => Interlocked.Add(ref totalCount, localCount) ); Console.WriteLine("总计数: " + totalCount);
注意:如果需要全局递增的序号,第一种Interlocked方案最直接;如果追求性能,第三种本地状态方案更适合大规模迭代。
内容的提问来源于stack exchange,提问作者TravTanker
相关产品推荐
相关产品推荐

