【问题标题】:Does C# Parallel.ForEach use the same thread for iterations of the collectionC# Parallel.ForEach 是否对集合的迭代使用相同的线程
【发布时间】:2014-12-20 15:14:42
【问题描述】:

我有一个要传递给 Parallel.ForEach 的 IEnumerable,但 IEnumerable 是使用 C# 方法和 yield 实现的,并且枚举器中的代码可能不是线程安全的。计划在此使用 Parallel.ForEach。我假设 ForEach 的 IEnumerable 的实际内部迭代始终在同一个线程上,然后 ForEach 将每个项目传递给潜在的其他线程进行处理。这是正确的吗?

我读到了 IEnumerable 迭代使用线程本地存储的地方,访问同一个 IEnumerable 的不同线程每个都有自己的迭代当前、下一个等。这就是为什么我假设 ForEach 实际上会在同一个线程上进行所有迭代.

我至少知道迭代不在调用 Parallel.ForEach 的同一线程上。有点希望这是因为我需要为正在执行迭代的线程做一些线程初始化。所以这里可能有一个问题,将我的初始化注入到执行迭代的线程中。

【问题讨论】:

  • 我想你不想在线程不安全的东西上使用多线程。
  • 我希望枚举在单个线程上是线程安全的,但单个元素的实际处理可以在多个线程上进行。
  • 因此,从 cmets 听起来它正在迭代同一线程中的元素。现在,只希望它会在主线程上完成,或者,我猜想某种方式来挂钩它。我将尝试用 yield 包装另一个方法,看看是否可以在产生每个元素之前初始化线程
  • 我创建了一个产生每个项目的函数,并在输入时放置日志以及每个收益,我看到它是从多个线程调用的。所以,不是从单个线程调用的。想知道这是否意味着我传递给 ForEach 的枚举必须是线程安全的。

标签: c# multithreading foreach parallel-processing task-parallel-library


【解决方案1】:

不可能编写一个可以安全地从多个线程迭代的IEnumerator。 API 本质上反对允许此类行为。由于这个 Parallel.ForEach 没有可能的选择,只能从单个线程枚举序列。为了将迭代器直接暴露给每个生成的线程,它需要使用与 IEnumerator/IEnumerable 暴露不同的 API。

【讨论】:

  • 我不确定这是否准确。只要 MoveNext 调用是同步的,就可以安全地从不同的线程枚举,这很容易破坏任何依赖于线程本地存储的东西。
  • @Stilgar 不,您无法从多个线程中安全地枚举枚举器。首先,这样做的全部意义在于对MoveNext 的调用不会同步。这就是将它们设计为从多个线程安全枚举的意义。此外,我们永远无法确定Current 的值是否实际应用于线程所做的移动;您会看到两个线程同时移动迭代器,然后都使用第二次移动的当前值。
  • 据我了解,如果您确实从多个线程进行迭代,每个线程都会有自己的私有迭代步骤。例如如果枚举中有 10 个元素,每个线程将独立地遍历这 10 个元素中的每一个(而不是一个线程得到一些,另一个线程得到一些,等等)
  • @Ron 它在单个线程上进行所有迭代,这正是我告诉你它正在做的事情,因为它不可能从多个线程中进行迭代。
  • @YuvalItzchakov 是的。你永远无法知道Current 在并发 MoveNext 调用的情况下会返回给你什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-06
  • 2011-05-29
  • 2014-10-03
  • 2014-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多