Передача асинхронного метода в Parallel.ForEach - PullRequest
0 голосов
/ 27 ноября 2018

Я читал этот пост о Parallel.ForEach, где было указано, что "Parallel.ForEach не совместим с передачей в асинхронном методе."

Итак, для проверки я пишуэтот код:

static async Task Main(string[] args)
{
    var results = new ConcurrentDictionary<string, int>();

    Parallel.ForEach(Enumerable.Range(0, 100), async index =>
    {
        var res = await DoAsyncJob(index);
        results.TryAdd(index.ToString(), res);
    });         

    Console.ReadLine();
}

static async Task<int> DoAsyncJob(int i)
{
    Thread.Sleep(100);
    return await Task.FromResult(i * 10);
}

Этот код заполняет словарь results одновременно.

Кстати, я создал словарь типа ConcurrentDictionary<string, int>, потому что в случае, если у меня есть ConcurrentDictionary<int, int>, когда я исследую его элементы в режиме отладки, я вижу, что элементы сортируются по ключу, и я думал, что элементы былиследовательно, добавлено.

Итак, я хочу знать, правильный ли мой код?Если он «не совместим с передачей в асинхронном методе», почему он работает хорошо?

Ответы [ 3 ]

0 голосов
/ 27 ноября 2018

Этот код работает только потому, что DoAsyncJob на самом деле не асинхронный метод.async не заставляет метод работать асинхронно.Ожидание выполненного задания, такого как возвращаемое Task.FromResult, также является синхронным.async Task Main не содержит асинхронного кода, что приводит к предупреждению компилятора.

Пример, демонстрирующий, как Parallel.ForEach не работает с асинхронными методами, должен вызывать реальный асинхронный метод:

    static async Task Main(string[] args)
    {
        var results = new ConcurrentDictionary<string, int>();

        Parallel.ForEach(Enumerable.Range(0, 100), async index =>
        {
            var res = await DoAsyncJob(index);
            results.TryAdd(index.ToString(), res);
        });  
        Console.WriteLine($"Items in dictionary {results.Count}");
    }

    static async Task<int> DoAsyncJob(int i)
    {
        await Task.Delay(100);
        return i * 10;
    }

Результат будет

Items in dictionary 0

Parallel.ForEach не имеет перегрузки, принимая Func<Task>, он принимает только Action делегатов.Это означает, что он не может ожидать никаких асинхронных операций.

async index принимается, потому что это неявно async void делегат .Что касается Parallel.ForEach, то это просто Action<int>.

В результате Parallel.ForEach запускает 100 задач и никогда не ждет их завершения.Вот почему словарь по-прежнему пуст после завершения работы приложения.

0 голосов
/ 27 ноября 2018

Если у вас уже есть асинхронная работа, вам не нужно Parallel.ForEach:

static async Task Main(string[] args)
{

    var results = await new Task.WhenAll(
        Enumerable.Range(0, 100)
        Select(i => DoAsyncJob(I)));

    Console.ReadLine();
}

Что касается вашей асинхронной работы, вы можете либо идти асинхронно до конца:

static async Task<int> DoAsyncJob(int i)
{
    await Task.Delay(100);
    return await Task.FromResult(i * 10);
}

А еще лучше:

static async Task<int> DoAsyncJob(int i)
{
    await Task.Delay(100);
    return i * 10;
}

или не совсем:

static Task<int> DoAsyncJob(int i)
{
    Thread.Sleep(100);
    return Task.FromResult(i * 10);
}
0 голосов
/ 27 ноября 2018

Метод async запускается и возвращает Task.

Ваш код здесь

Parallel.ForEach(Enumerable.Range(0, 100), async index =>
{
    var res = await DoAsyncJob(index);
    results.TryAdd(index.ToString(), res);
});        

запускает асинхронные методы 100 раз параллельно.То есть он распараллеливает задачу создание , а не всю задачу.Ко времени возврата ForEach ваши задачи уже выполняются, но они не обязательно завершены.

Ваш код работает, потому что DoAsyncJob() на самом деле не асинхронный - ваша задача завершается после возврата.Thread.Sleep() - это синхронный метод.Task.Delay() является его асинхронным эквивалентом.

Поймите разницу между Операциями с привязкой к процессору и вводом / выводом .Как уже отмечали другие, параллелизм (и Parallel.ForEach) предназначен для операций с процессором, а асинхронное программирование не подходит.

...