2016-04-06 3 views
4

Все еще переживая этап обучения с C# и столкнувшись с вопросом, в котором мне была нужна помощь. С учетом следующего кода:Рефакторинг Async/Ожидание параллельной обработки

private async Task<String> PrintTask() 
{ 
    await Task.Delay(3000); 
    return "Hello"; 
} 

private async void SayHelloTwice() 
{ 
    string firstHello = await PrintTask(); 
    string secondHello = await PrintTask(); 

    Console.WriteLine(firstHello); 
    Console.WriteLine(secondHello); 
} 

Прямо сейчас программе SayHelloTwice() требуется 6 секунд. Тем не менее, я хочу, чтобы задачи поиска выполнялись параллельно, чтобы выполнить только 3 секунды. Как бы мне реорганизовать мой код для достижения этого? Благодаря!

+1

Просто комментарий по терминологии: "параллельной" подразумевает несколько потоков. Вы хотите асинхронный * параллелизм *, а не * параллелизм *. –

ответ

4

Правильный способ сделать это (без риска тупиков) является использование Task.WhenAll

private async void SayHelloTwice() 
{ 
    string[] results = await Task.WhenAll(
     PrintTask(), 
     PrintTask()); 

    Console.WriteLine(results[0]); 
    Console.WriteLine(results[1]); 
} 

Довольно часто библиотечные писатели будут стремиться сделать «обратный вызов» кода в задаче запуска на исходном потоке, который называется Это. Это часто происходит из-за того, что объекты могут быть доступны только из одного потока.

При использовании блокирующих вызовов, таких как Task.Result и Task.WaitAll, поток приостановлен (не может выполнять дополнительную работу) до тех пор, пока не завершится Task.

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

Итак, в первом случае «Задача» Outter удерживает Thread и ждет завершения.

Второй случай, внутренняя «задача» ожидает завершения потока.

Эрго ни когда-нибудь не закроется.

+0

Если вы собираетесь ввести угрозу «тупиков», возможно, также объясните, когда и где это проблема. – NPSF3000

+0

@ NPSF3000 У меня уже был удаленный ответ .... Дайте мне секунду ... – Aron

+2

Этот ответ верный. Я просто чувствую, что все взаимоблокировки являются излишними, так как это не имеет никакого отношения к актуальному вопросу. – Itsik

3

В общем, вы хотите запустить обе задачи и подождать потом. Несложный способ:

private async void SayHelloTwice() 
{ 
    // Start the tasks, don't wait 
    Task<string> firstHello = PrintTask(); 
    Task<string> secondHello = PrintTask(); 

    // Wait for results 
    string firstResult = await firstHello; 
    string secondResult = await secondHello; 

    // Print results 
    Console.WriteLine(firstResult); 
    Console.WriteLine(secondResult); 
} 

Что здесь происходит вызов PrintTask() начнет выполнение метода и когда выполнение достигает первый await, который делает фактическую асинхронную операцию, бегущая задача будет возвращена и назначена to firstHello. То же самое касается secondHello. Затем вы ждете завершения и распечатки результата.

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

+0

Как это отличается от того, что имеет OP? –

+1

Обе задачи запускаются в одно и то же время, в отличие от ожидания завершения первой задачи перед запуском второго – Itsik

+0

. А теперь я вижу это в комментариях. Благодарю. –

3
  • Вы можете использовать Task.WhenAll ждать несколько задач.
  • Вы также должны предпочесть возвращая Task, а что void (async/await - when to return a Task vs void?):

    private static async Task SayHelloTwice() 
    { 
        var hellos = await Task.WhenAll(PrintTask(), PrintTask()); 
        Console.WriteLine(hellos[0]); 
        Console.WriteLine(hellos[1]); 
    } 
    
Смежные вопросы