使用AsParallel的IEnumerable方法

时间:2013-10-24 20:26:40

标签: 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();

有效,但中间有一些空行,因为有些线程“太晚了”并进入第一次收益率返回。

我尝试在任何地方放置更多的锁,但是我无法通过并行来实现这项工作。

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

1 个答案:

答案 0 :(得分:3)

问题是使用迭代器是惰性求值的,所以你返回一个从多个线程中使用的延迟求值的迭代器。

您可以通过重写方法来解决此问题:

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类型。