我们有这样的代码:
var intList = new List<int>{1,2,3};
var asyncEnumerables = intList.Select(Foo);
private async IAsyncEnumerable<int> Foo(int a)
{
while (true)
{
await Task.Delay(5000);
yield return a;
}
}
我需要为每个await foreach
的条目开始asyncEnumerable
。每个循环迭代都应该互相等待,并且在完成每个迭代后,我需要收集每个迭代的数据并通过另一种方法进行处理。
我能以某种方式通过TPL实现吗?否则,你不能给我一些想法吗?
答案 0 :(得分:3)
最适合我的是this回购中的Zip
函数(81行)
我正在这样使用它
var intList = new List<int> { 1, 2, 3 };
var asyncEnumerables = intList.Select(RunAsyncIterations);
var enumerableToIterate = async_enumerable_dotnet.AsyncEnumerable.Zip(s => s, asyncEnumerables.ToArray());
await foreach (int[] enumerablesConcatenation in enumerableToIterate)
{
Console.WriteLine(enumerablesConcatenation.Sum()); //Sum returns 6
await Task.Delay(2000);
}
static async IAsyncEnumerable<int> RunAsyncIterations(int i)
{
while (true)
yield return i;
}
答案 1 :(得分:1)
这里是您可以使用的通用方法Zip
,已实现为iterator。 cancellationToken
用EnumeratorCancellation
属性修饰,因此生成的IAsyncEnumerable
是WithCancellation
友好的。
using System.Runtime.CompilerServices;
public static async IAsyncEnumerable<TSource[]> Zip<TSource>(
IEnumerable<IAsyncEnumerable<TSource>> sources,
[EnumeratorCancellation]CancellationToken cancellationToken = default)
{
var enumerators = sources.Select(x => x.GetAsyncEnumerator()).ToArray();
try
{
while (true)
{
var array = new TSource[enumerators.Length];
for (int i = 0; i < enumerators.Length; i++)
{
if (!await enumerators[i].MoveNextAsync(cancellationToken)) yield break;
array[i] = enumerators[i].Current;
}
yield return array;
}
}
finally
{
foreach (var enumerator in enumerators)
{
await enumerator.DisposeAsync();
}
}
}
用法示例:
await foreach (int[] result in Zip(asyncEnumerables))
{
Console.WriteLine($"Result: {String.Join(", ", result)}");
}