【问题标题】:Parallel.ForEach() not getting expected resultsParallel.ForEach() 没有得到预期的结果
【发布时间】:2017-03-31 17:34:46
【问题描述】:

在我的 web api 控制器中,我使用 Parallel.ForEach() 循环遍历一个列表。我有一个计数器,我在 Parallel.ForEach 代码中递增。我注意到每次运行它时计数器都是一个可变数字,它从来没有像我用 Parallel.ForEach() 循环的列表一样高。似乎 Parallel.ForEach() 并没有在完​​成循环所有元素之前等待返回。

// get all the new records from the csv
var newData = csv.GetRecords<MyEFTable>().ToArray();
int count = 0;
Parallel.ForEach(newData, (d) => {
  count++});

newData 有 6588 个项目,计数通常在 3400 个左右,但每次都是可变的。这很奇怪。

【问题讨论】:

  • count++ 不是线程安全的。将其替换为Interlocked.Incremenet(ref count)

标签: c# parallel.foreach


【解决方案1】:

您正在进入竞争状态。您需要使用var newCount = Interlocked.Increment(ref count); 在多线程环境中安全地递增变量。 newCount 变量是递增的计数器。

发生这种情况的原因是count++ 不是原子的。它实际上是三个操作:获取值,加 1,然后将其存储回来。如果这三件事同时发生,事情就会变得乱七八糟。

在处理线程时,确保每个线程不会处理相同的数据至关重要,否则它们会相互挤压。

【讨论】:

  • 那么添加到列表也不是线程安全的吗?我使用柜台的原因是为了看看里面发生了什么。我有比这更多的代码,但正在分解它以查看发生了什么,因为添加到列表也没有给出正确的结果。
  • 可能。您可以查看 List 类的 MSDN 文档以查看(提示:列表方法是 not 线程安全的)。因此,如果您在多个线程中操作列表,您将 a) 需要切换到线程安全的集合类型,例如 ConcurrentBag,或 b) 在访问共享资源的代码周围使用 lock (SyncLock) .
  • 再次,发生的事情是一个线程添加一个项目,而另一个线程同时执行相同的操作。第一个线程获取当前项目,另一个线程也是如此。然后第一个线程添加一个项目,并存储结果。好吧,如果第二个线程也这样做,它仍然在其项目列表的原始“版本”上运行。因此,您可以添加项目,但让这些添加被“覆盖”。
  • 好的,这就是正在发生的事情。我看到有并发集合来防止这种情况。这就是我需要的。当它允许我时会接受你的回答。感谢您为我指明正确的方向。
  • 添加到 Parallel.ForEach 中的列表不是线程安全的。在写入之前使用线程安全的集合或锁。
猜你喜欢
  • 1970-01-01
  • 2022-01-24
  • 2021-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多