使用Parallel.ForEach()实现yield返回的线程安全性

本文关键字:返回 线程 安全性 yield 实现 Parallel ForEach 使用 | 更新日期: 2023-09-27 18:17:35

考虑下面的代码示例,它创建了一个可枚举的整数集合,并并行处理它:

using System.Collections.Generic;
using System.Threading.Tasks;
public class Program
{
    public static void Main()
    {
        Parallel.ForEach(CreateItems(100), item => ProcessItem(item));
    }
    private static IEnumerable<int> CreateItems(int count)
    {
        for (int i = 0; i < count; i++)
        {
            yield return i;
        }
    }
    private static void ProcessItem(int item)
    {
        // Do something
    }
}

是否保证Parallel.ForEach()生成的工作线程每个都得到一个不同的项目,或者是否需要一些锁定机制来增加和返回i ?

使用Parallel.ForEach()实现yield返回的线程安全性

Parallel.ForEach<TSource>,当TSourceIEnumerable<T>时,为IEnumerable<T>创建一个分区,该分区包含自己的内部锁定机制,因此您不需要在迭代器中实现任何线程安全。

每当工作线程请求一个项块时,分区器将创建一个内部枚举器,它:

  1. 获取共享锁
  2. 遍历源(从它离开的地方开始)以检索项块,将项保存在私有数组
  3. 中。
  4. 释放锁,以便其他块请求可以被满足。
  5. 从它的私有数组提供工作线程。

如您所见,为了进行分区,对IEnumerable<T>的运行是顺序的(通过共享锁访问),并且分区是并行处理的。

TPL和PLINQ使用分区器的概念。

Partitioner是一种继承Partitioner<TSource>的类型,用于将源序列分割成许多部分(或分区)。内置分区器被设计成将源序列分割成不重叠的分区。