平行foreach的行为不一致

本文关键字:不一致 foreach 平行 | 更新日期: 2023-09-27 18:28:14

我有一个对象集合,需要通过一组规则来运行这些对象,以确定这些对象是否符合/匹配给定的规则集合。

最初这是作为SqlLink语句编写的:

从对象中的x来自规则中的y

我首先将该语句转换为ForEach/内部ForEach语句。结果与预期结果一致。

在许多其他情况下使用Parallel.ForEach后,我认为在这里使用它是一个好地方。

objects.ForEach(o => {
    var obj = o.Value;
    var ruleCount = 0;
    //eventRules.ForEach(r => {
    Parallel.ForEach(eventRules, r => {
        var matchedDetail = r.GetMatchDetails(obj, _ruleContext);
        var matched = matchedDetail.Matched;
        if (matchedDetail.Matched)
        {
            var result = CreateResult(obj, r, matchedDetail);
            matchedResults.Add(result);
        }
        ruleCount += 1;
    });
    Console.WriteLine($"{obj.object_id} {eventRules.Count()} {ruleCount}");
});

84052 83 82
35135 83 82
37576 83 83
38772 83 81
80513 83 81
95824 83 83
99402 83 82
24626 83 83
30711 83 82
96613 83 83
63487 83 83
78497 83 83
81404 83 83
93719 83 82
36600 83 83
68544 83 83
78685 83 81

在注意到添加Parallel.ForEach后结果不匹配后,我添加了额外的ruleCount标记和输出。这表明随机地我将看到3或4个规则没有被运行/返回。把标准的ForEach换回来,结果是跑得非常一致,尽管速度较慢。我从MSDN文档和其他来源了解到,Parallel.ForEach应该等到所有操作完成后再返回调用程序。

顺便说一句,我用4.5.1和4.6.1测试了这一点,它们都表现出了相同的行为。

平行foreach的行为不一致

执行的某些操作不是线程安全的。

其中之一是CCD_ 1。您可以将其替换为Interlocked.Increment,以使其原子化和线程安全。

Interlocked.Increment(ref ruleCount);

另一个(可能的)是matchedResults.Add(result);。我看不到matchedResults集合的类型,但您应该确保它是线程安全的集合(例如ConcurrentQueue)。您也可以使用lock来同步对常规集合的访问,但使用线程安全集合是一个更好的主意。