Bor*_*ign 5 .net c# linq parallel-processing asynchronous
I have an IEnumerable<IEnumerable<T>> method called Batch that works like
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));
Run Code Online (Sandbox Code Playgroud)
-->
1,2
4,8
10,-4
3
Run Code Online (Sandbox Code Playgroud)
The problem I've having is that I'm to optimize something like
foreach(var batch in batches)
ExecuteBatch(batch);
Run Code Online (Sandbox Code Playgroud)
by
Task[] tasks = batches.Select(batch => Task.Factory.StartNew(() => ExecuteBatch(batch))).ToArray();
Task.WaitAll(tasks);
Run Code Online (Sandbox Code Playgroud)
or
Action[] executions = batches.Select(batch => new Action(() => ExecuteBatch(batch))).ToArray();
var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.Invoke(options, executions);
Run Code Online (Sandbox Code Playgroud)
(because ExecuteBatch is a long-running operation involving IO)
then I notice that each batch gets screwed up, is only 1 element which is default(int). Any idea what's happening or how to fix it?
Batch:
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());
}
Run Code Online (Sandbox Code Playgroud)
正如评论中所指出的,您的实际问题是您的实施Batch。
这段代码:
for(var mover = source.GetEnumerator(); ;)
{
if(!mover.MoveNext())
yield break;
yield return LimitMoves(mover, size);
}
Run Code Online (Sandbox Code Playgroud)
当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;
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用MoreLINQ - 它带有一个Batch实现。你可以在这里看到他们的实现
| 归档时间: |
|
| 查看次数: |
290 次 |
| 最近记录: |