You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Parallel.ForEach搜索无法返回正确值,如何解决并行查找最值错误问题

问题根因

  1. 存在竞态条件:现有代码计算Math.Min(pMin, min)、Math.Max(pMax, max)时,读取的全局pMin/pMax为瞬时值,从读取完成到执行Interlocked.Exchange的间隙,其他线程可能已经更新了全局变量,导致本次写入的结果基于旧值计算,会覆盖其他线程已经写入的更小值/更大值,最终结果错误。
  2. 类型转换不安全:使用Unsafe.As<short, int>将2字节的short强转为4字节的int执行原子操作,存在内存越界访问风险,可能触发不可预知的异常。

修复方案

推荐使用Parallel.ForEach自带的本地线程状态聚合重载,无需手动处理复杂的原子操作,性能更高且无竞态问题。该方案逻辑为:每个分区线程先独立计算自己负责区间的最大最小值,所有分区计算完成后再统一合并全局结果,锁竞争极低。
修复后的并行计算段代码如下:

// 生成测试列表、顺序搜索部分代码保持不变,仅替换并行搜索逻辑
short pMin = short.MaxValue;
short pMax = 0;
var rangePortioner = Partitioner.Create(0, test.Count);
object lockObj = new object();

timer.Restart();
Parallel.ForEach(
    rangePortioner,
    // 初始化每个线程的本地最大最小值
    () => (LocalMin: short.MaxValue, LocalMax: (short)0),
    // 分区内遍历,仅更新本地变量,无全局竞争
    (range, loopState, localState) =>
    {
        for (int i = range.Item1; i < range.Item2; i++)
        {
            localState.LocalMin = Math.Min(localState.LocalMin, test[i]);
            localState.LocalMax = Math.Max(localState.LocalMax, test[i]);
        }
        return localState;
    },
    // 合并每个线程的本地结果到全局变量
    (localState) =>
    {
        lock (lockObj)
        {
            pMin = Math.Min(pMin, localState.LocalMin);
            pMax = Math.Max(pMax, localState.LocalMax);
        }
    });
timer.Stop();

如果坚持使用Interlocked实现,需要改为循环判断更新的逻辑,避免竞态:

// 最小值更新逻辑,最大值同理
int oldVal, newVal;
do
{
    oldVal = pMin;
    newVal = Math.Min(oldVal, min);
} while (Interlocked.CompareExchange(ref Unsafe.As<short, int>(ref pMin), newVal, oldVal) != oldVal);

内容的提问来源于stack exchange,提问作者master_ruko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 00:57:04