c# - 带有 AsParallel 的 IEnumerable 方法

标签 c# multithreading linq thread-safety

我得到了以下扩展方法:

static class ExtensionMethods
{
    public static IEnumerable<IEnumerable<T>> Subsequencise<T>(
        this IEnumerable<T> input,
        int subsequenceLength)
    {
        var enumerator = input.GetEnumerator();
        SubsequenciseParameter parameter = new SubsequenciseParameter
            { 
                Next = enumerator.MoveNext()
            };

        while (parameter.Next)
                yield return getSubSequence(
                    enumerator,
                    subsequenceLength,
                    parameter);         
    }

    private static IEnumerable<T> getSubSequence<T>(
        IEnumerator<T> enumerator,
        int subsequenceLength,
        SubsequenciseParameter parameter)
    {
        do
        {
            lock (enumerator) // this lock makes it "work"
            {                 // removing this causes exceptions.
                if (parameter.Next)
                    yield return enumerator.Current;
            }

        } while ((parameter.Next = enumerator.MoveNext())
                  && --subsequenceLength > 0);
    }

    // Needed since you cant use out or ref in yield-return methods...
    class SubsequenciseParameter
    {
        public bool Next { get; set; }
    }
}

其目的是将序列拆分为给定大小的子序列。

这样调用它:

foreach (var sub in "abcdefghijklmnopqrstuvwxyz"
                    .Subsequencise(3)
                    .**AsParallel**()
                    .Select(sub =>new String(sub.ToArray()))
{
    Console.WriteLine(sub);
}

Console.ReadKey();

可以工作,但是中间有一些空行,因为某些线程“太晚了”并进入第一个 yield 返回。

我尝试在各处放置更多的锁,但是我无法与并行结合使这项工作正确。

很明显,这个例子根本不能证明使用 asparallel 是合理的。这只是为了演示如何调用该方法。

最佳答案

问题在于使用迭代器是惰性计算的,因此您返回一个从多个线程使用的惰性计算迭代器。

您可以通过重写方法来解决此问题,如下所示:

public static IEnumerable<IEnumerable<T>> Subsequencise<T>(this IEnumerable<T> input, int subsequenceLength)
{
    var syncObj = new object();
    var enumerator = input.GetEnumerator();
    if (!enumerator.MoveNext())
    {
        yield break;
    }

    List<T> currentList = new List<T> { enumerator.Current };
    int length = 1;
    while (enumerator.MoveNext())
    {
        if (length == subsequenceLength)
        {
            length = 0;
            yield return currentList;
            currentList = new List<T>();                
        }
        currentList.Add(enumerator.Current);
        ++length;
    }
    yield return currentList;
}

这执行相同的功能,但不使用迭代器来实现“嵌套”IEnumerable<T> ,回避问题。请注意,这也避免了锁定以及自定义 SubsequenciseParameter类型。

关于c# - 带有 AsParallel 的 IEnumerable 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19575966/

相关文章:

c# - Linq to SQL,多对多设计

C++ - 在后台 POSIX 线程上以固定增量时间循环

c# - 使用 Linq 解析 XML 时,只会获取一个对象

c# - AssemblyInfo.cs 内的版本号在 Visual Studio 外部更新

C# 笔.DashPattern

c# - 在以对象为值的 C# 哈希表中,如何返回对象值

c++ - C++ 11 标准是否保证 std::atomic<> 被实现为无锁操作?

c++ - 提升智能指针和线程

asp.net-mvc-3 - 在 LINQ 查询中调用方法

c# - 为什么 EF 在比较 null 变量时不返回任何结果?