2016-07-28 7 views
6

У меня есть метод async, GetExpensiveThing(), который выполняет некоторую дорогостоящую работу ввода-вывода. Вот как это я использую:Выполнение параллельных асинхронных методов

// Serial execution 
public async Task<List<Thing>> GetThings() 
{ 
    var first = await GetExpensiveThing(); 
    var second = await GetExpensiveThing(); 
    return new List<Thing>() { first, second }; 
} 

Но поскольку это дорогостоящий метод, я хочу выполнить эти вызовы параллельно. Я бы подумал, перемещающих Awaits решили бы это:

// Serial execution 
public async Task<List<Thing>> GetThings() 
{ 
    var first = GetExpensiveThing(); 
    var second = GetExpensiveThing(); 
    return new List<Thing>() { await first, await second }; 
} 

Это не сработало, так что я завернул их в некоторых задачах и это работает:

// Parallel execution 
public async Task<List<Thing>> GetThings() 
{ 
    var first = Task.Run(() => 
    { 
     return GetExpensiveThing(); 
    }); 

    var second = Task.Run(() => 
    { 
     return GetExpensiveThing(); 
    }); 

    return new List<Thing>() { first.Result, second.Result }; 
} 

Я даже пытался играть с ожидающей и асинхронно и вокруг задач, но это действительно запутывало, и мне не повезло.

Есть ли возможность запускать асинхронные методы параллельно или же задачи являются хорошим подходом?

ответ

14

Есть ли возможность запускать асинхронные методы параллельно или же задачи являются хорошим подходом?

Да, «лучший» подход заключается в использовании метода Task.WhenAll. Тем не менее, ваш второй подход должен был работать параллельно. Я создал .NET Fiddle, это должно помочь пролить некоторый свет. Второй подход должен выполняться параллельно. Моя скрипка доказывает это!

Рассмотрим следующий пример:

public Task<Thing[]> GetThingsAsync() 
{ 
    var first = GetExpensiveThingAsync(); 
    var second = GetExpensiveThingAsync(); 

    return Task.WhenAll(first, second); 
} 

Примечание

Предпочтительно использовать суффикс "Async", вместо того, чтобы GetThings и GetExpensiveThing - мы должны иметь GetThingsAsync и GetExpensiveThingAsync соответственно - source ,

+0

'ждут задачи.WhenAll' вернет 'Thing []', поэтому нет необходимости в 'Result' (фактически,' Result' будет обертывать исключения, вы должны использовать 'await' или использовать результат' waitait Task.WhenAll'). –

+1

Хороший улов, спасибо - рано, и я «ждал» своего кофе. :) –

+0

Хотя это в целом правильный способ, что это делает, что 'return new List () {ждать сначала, ждать второй};' нет? Если OP сказал, что это не сработало, должно быть что-то еще в игре ... – yaakov

2

Вы можете Task.WhenAll, который возвращает, когда все зависящие задачи выполняются

Проверьте этот вопрос here для справки

3

Если GetExpensiveThingэто правильно асинхронный (то есть он не делает каких-либо операций ввода-вывода или процессор работайте синхронно), ваше второе решение вызова обоих методов, а затем ожидающих результатов должно работать. Вы могли бы также использовать Task.WhenAll.

Однако, если это не так, вы можете получить лучшие результаты, размещая каждую задачу на токарно-пула и используя Task.WhenAll комбинатора, например:

public Task<IList<Thing>> GetThings() => 
    Task.WhenAll(Task.Run(() => GetExpensiveThing()), Task.Run(() => GetExpensiveThing())); 

(Обратите внимание, я изменил тип возвращаемого значения IList чтобы избежать await s в целом.)

Вам следует избегать использования имущества Result. Это заставляет поток вызывающего абонента блокировать и ждать завершения задачи, в отличие от await или Task.WhenAll, которые используют продолжения.

Смежные вопросы