可能であれば、私は並行して起動されたタスクのためのasync-列挙子を作成したいです。そのため、最初に完了することは列挙の最初の要素です。フィニッシュの2番目の要素は、列挙型などの2番目の要素です。
_public static async IAsyncEnumerable<T> ParallelEnumerateAsync(this IEnumerable<Task<T>> coldAsyncTasks)
{
// ...
}
_
私はContinueWith
と_Queue<T>
_を使用している方法があると思いますが、実装に完全に信頼していません。
ASYNCストリーム(IASYNCENUMERABLE)を実行し、Select
を並列に実行したい場合は、最初の終了は最初のものです。
/// <summary>
/// Runs the selectors in parallel and yields in completion order
/// </summary>
public static async IAsyncEnumerable<TOut> SelectParallel<TIn, TOut>(
this IAsyncEnumerable<TIn> source,
Func<TIn, Task<TOut>> selector)
{
if (source == null)
{
throw new InvalidOperationException("Source is null");
}
var enumerator = source.GetAsyncEnumerator();
var sourceFinished = false;
var tasks = new HashSet<Task<TOut>>();
Task<bool> sourceMoveTask = null;
Task<Task<TOut>> pipeCompletionTask = null;
try
{
while (!sourceFinished || tasks.Any())
{
if (sourceMoveTask == null && !sourceFinished)
{
sourceMoveTask = enumerator.MoveNextAsync().AsTask();
}
if (pipeCompletionTask == null && tasks.Any())
{
pipeCompletionTask = Task.WhenAny<TOut>(tasks);
}
var coreTasks = new Task[] { pipeCompletionTask, sourceMoveTask }
.Where(t => t != null)
.ToList();
if (!coreTasks.Any())
{
break;
}
await Task.WhenAny(coreTasks);
if (sourceMoveTask != null && sourceMoveTask.IsCompleted)
{
sourceFinished = !sourceMoveTask.Result;
if (!sourceFinished)
{
try
{
tasks.Add(selector(enumerator.Current));
}
catch { }
}
sourceMoveTask = null;
}
if (pipeCompletionTask != null && pipeCompletionTask.IsCompleted)
{
var completedTask = pipeCompletionTask.Result;
if (completedTask.IsCompletedSuccessfully)
{
yield return completedTask.Result;
}
tasks.Remove(completedTask);
pipeCompletionTask = null;
}
}
}
finally
{
await enumerator.DisposeAsync();
}
}
_
次のように使用できます。
static async Task Main(string[] args)
{
var source = GetIds();
var strs = source.SelectParallel(Map);
await foreach (var str in strs)
{
Console.WriteLine(str);
}
}
static async IAsyncEnumerable<int> GetIds()
{
foreach (var i in Enumerable.Range(1, 20))
{
await Task.Delay(200);
yield return i;
}
}
static async Task<string> Map(int id)
{
await Task.Delay(rnd.Next(1000, 2000));
return $"{id}_{Thread.CurrentThread.ManagedThreadId}";
}
_
可能な出力:
[6:31:03 PM] 1_5
[6:31:03 PM] 2_6
[6:31:04 PM] 3_6
[6:31:04 PM] 6_4
[6:31:04 PM] 5_4
[6:31:04 PM] 4_5
[6:31:05 PM] 8_6
[6:31:05 PM] 7_6
[6:31:05 PM] 11_6
[6:31:05 PM] 10_4
[6:31:05 PM] 9_6
[6:31:06 PM] 14_6
[6:31:06 PM] 12_4
[6:31:06 PM] 13_4
[6:31:06 PM] 15_4
[6:31:07 PM] 17_4
[6:31:07 PM] 20_4
[6:31:07 PM] 16_6
[6:31:07 PM] 18_6
[6:31:08 PM] 19_6
_