并行调用IEnumerable的元素

时间:2017-07-19 01:56:30

标签: c# .net linq asynchronous parallel-processing

我有一个名为IEnumerable<IEnumerable<T>>的{​​{1}}方法,其工作方式与

类似
Batch

- &GT;

var list = new List<int>() { 1, 2, 4, 8, 10, -4, 3 }; 
var batches = list.Batch(2); 
foreach(var batch in batches)
    Console.WriteLine(string.Join(",", batch));

我遇到的问题是我要优化像

这样的东西
1,2
4,8
10,-4
3

通过

foreach(var batch in batches)
    ExecuteBatch(batch);

Task[] tasks = batches.Select(batch => Task.Factory.StartNew(() => ExecuteBatch(batch))).ToArray();
Task.WaitAll(tasks);

(因为Action[] executions = batches.Select(batch => new Action(() => ExecuteBatch(batch))).ToArray(); var options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; Parallel.Invoke(options, executions); 是一个涉及IO的长时间运行的操作

然后我注意到每个ExecuteBatch被搞砸了,只有一个元素是batch。知道发生了什么或如何解决它?

批处理:

default(int)

1 个答案:

答案 0 :(得分:3)

正如评论中所述,您的实际问题是Batch

的实施

此代码:

for(var mover = source.GetEnumerator(); ;)
{
    if(!mover.MoveNext())
        yield break;
    yield return LimitMoves(mover, size);
}

Batch具体化时,此代码将不断调用MoveNext(),直到可枚举用尽为止。 LimitMoves()使用相同的迭代器,并且 lazily 被调用。由于Batch耗尽了可枚举,LimitMoves()将永远不会发出项目。 (实际上,它只会发出default(T),因为它总是返回mover.Current,一旦枚举完成,它将是default(T)

这是Batch的实现,它将在实现时(因此并行)时起作用。

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
    var mover = source.GetEnumerator();
    var currentSet = new List<T>();
    while (mover.MoveNext())
    {
        currentSet.Add(mover.Current);
        if (currentSet.Count >= size)
        {   
            yield return currentSet;
            currentSet = new List<T>();
        }
    }
    if (currentSet.Count > 0)
        yield return currentSet;
}

或者,您可以使用MoreLINQ - Batch实施附带的here。您可以看到他们的实施{{3}}