c# - 并行调用 IEnumerable 的元素

标签 c# .net linq asynchronous parallel-processing

我有一个 IEnumerable<IEnumerable<T>>方法称为 Batch就像

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);

(因为 ExecuteBatch 是涉及 IO 的长时间运行的操作)

然后我注意到每个 batch搞砸了,只有 1 个元素,即 default(int) .知道发生了什么或如何解决它吗?

批量:

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
    for(var mover = source.GetEnumerator(); ;)
    {
        if(!mover.MoveNext())
            yield break;
        yield return LimitMoves(mover, size);
    }
}
private static IEnumerable<T> LimitMoves<T>(IEnumerator<T> mover, int limit)
{
    do yield return mover.Current;
    while(--limit > 0 && mover.MoveNext());
}

最佳答案

如评论中所述,您的实际问题是您对 Batch 的实现。

这段代码:

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

Batch 实现时,此代码将不断调用 MoveNext() 直到枚举耗尽。 LimitMoves() 使用相同的迭代器,并且被延迟 调用。由于 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

关于c# - 并行调用 IEnumerable 的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45179750/

相关文章:

c# - 在 C# 中找出两个日期的差异

.net - .net 有 BigDateTime 类型吗?

c# - 如何在 C# 中旋转列表

c# - IEnumerable.Cast() 与 IEnumerable.Select() 中的转换

c# - 使用 NHibernate 和 memcached 帮助进行二级缓存

c# - 用户控件的水平列表(winforms)

c# - 在 WPF 中克隆 TabItem 最简单的方法是什么?

c# - LINQ 取查询中多个字段的平均值

C# 视频转换器库

c# - .NET:Type.Parse 不起作用?