1

Есть следующий кусок кода:

private static void WorkAsync()
{
    Parallel.For(0,
        20,
         async i =>
         {
             await Task.Delay(50000);
            Console.WriteLine(i);
        });
}

При вызове метода WorkAsync, он практически сразу возвращает управление вызывающему коду, хотя цикл For ещё не завершился. Я правильно понимаю, что асинхронная лямбда при каждом вызове возвращает управление вызывающему потоку и поэтому не происходит ожидание?

  • 4
    Да. Поэтому в дотнет 6+ наконец-то сделали Parallel.ForEachAsync – vitidev Jun 10 '23 at 15:33

1 Answers1

2

Ваша лямбда это async void, а async void невозможно ожидать.

Для асинхронных вызовов можно не использовать Parallel. Тем более у Parallel.For блокирующее синхронное ожидание, что для асинхронной среды не хорошо.

Как-то так это делается:

private static async Task WorkAsync()
{
    List<Task> tasks = new();
    for (int i = 0; i < 20; i++)
    {
        tasks.Add(JobAsync(i));
    }
    await Task.WhenAll(tasks);
}

private static async Task JobAsync(int i) { await Task.Delay(50000); Console.WriteLine(i); }

Вызов выглядит так:

await WorkAsync();

Если очень хочется, можно через лямбду:

private static async Task WorkAsync()
{
    List<Task> tasks = new();
    for (int i = 0; i < 20; i++)
    {
        tasks.Add(((Func<int, Task>)(async index =>
        {
            await Task.Delay(50000);
            Console.WriteLine(index);
        }))(i));
    }
    await Task.WhenAll(tasks);
}

Или так:

private static async Task WorkAsync()
{
    List<Task> tasks = new();
    Func<int, Task> func = async index =>
    {
        await Task.Delay(50000);
        Console.WriteLine(index);
    };
    for (int i = 0; i < 20; i++)
    {
        tasks.Add(func(i));
    }
    await Task.WhenAll(tasks);
}

Можно даже с ограничением на количество одновременно выполняемых задач: Массовые асинхронные вызовы с ограничением на количество параллельных без семафора

aepot
  • 49,560