与并行 foreach 的不一致行为
Inconsistent behavior with parallel foreach
我有一个对象集合,需要 运行 通过一组规则来确定这些对象是否 adhere/match 给定的规则集合。
最初这是写成 Sql Link 语句:
来自对象中的 x
从规则中的 y
我首先将该语句转换为 ForEach/inner 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 条规则不是 ran/returned。将标准的 ForEach 替换回来,结果非常一致,尽管速度较慢,运行。
我从 MSDN 文档和其他来源的理解表明 Parallel.ForEach 应该等到所有操作完成后再返回给调用者。
顺便说一句,我用 4.5.1 测试过这个,4.6.1 都表现出相同的行为。
执行的某些操作不是线程安全的。
其中之一是ruleCount += 1;
。您可以将其替换为 Interlocked.Increment
以使其成为原子和线程安全的。
Interlocked.Increment(ref ruleCount);
另一个(可能的)是matchedResults.Add(result);
。我看不到 matchedResults
集合的类型,但你应该确保它是 thread-safe collection (for example ConcurrentQueue
). You could also use lock
以同步对常规集合的访问,但使用线程安全集合是更好的主意。
我有一个对象集合,需要 运行 通过一组规则来确定这些对象是否 adhere/match 给定的规则集合。
最初这是写成 Sql Link 语句:
来自对象中的 x 从规则中的 y
我首先将该语句转换为 ForEach/inner 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 条规则不是 ran/returned。将标准的 ForEach 替换回来,结果非常一致,尽管速度较慢,运行。 我从 MSDN 文档和其他来源的理解表明 Parallel.ForEach 应该等到所有操作完成后再返回给调用者。
顺便说一句,我用 4.5.1 测试过这个,4.6.1 都表现出相同的行为。
执行的某些操作不是线程安全的。
其中之一是ruleCount += 1;
。您可以将其替换为 Interlocked.Increment
以使其成为原子和线程安全的。
Interlocked.Increment(ref ruleCount);
另一个(可能的)是matchedResults.Add(result);
。我看不到 matchedResults
集合的类型,但你应该确保它是 thread-safe collection (for example ConcurrentQueue
). You could also use lock
以同步对常规集合的访问,但使用线程安全集合是更好的主意。