2013-05-30 2 views
23

Я пишу веб-страницу, и она вызывает некоторые веб-службы. Призывы выглядит следующим образом:Когда использовать ключевое слово «ожидание»

var Data1 = await WebService1.Call(); 
var Data2 = await WebService2.Call(); 
var Data3 = await WebService3.Call(); 

Во время проверки кода, кто-то сказал, что я должен изменить его на:

var Task1 = WebService1.Call(); 
var Task2 = WebService2.Call(); 
var Task3 = WebService3.Call(); 

var Data1 = await Task1; 
var Data2 = await Task2; 
var Data3 = await Task3; 

Почему? Какая разница?

+0

Если это не проблема с копией, я не вижу причин, по которой достойный компилятор не будет оптимизировать дополнительное назначение. Хотя я не знаю достаточно о C#, чтобы сказать наверняка. Возможно, это всего лишь семантическая проблема, и ваша команда предпочитает четко указывать, что вызов является задачей, а результатом задачи являются данные? – JAB

+10

@JAB Это неправда. В семантическом значении этих двух фрагментов кода есть * драматическая * разница. – Servy

+0

@servy, какова ваша семантика для семантики? – nicolas

ответ

33

В первом фрагменте кода вы даже не , начиная с, второй вызов службы до тех пор, пока не завершится первый вызов службы (а также не начнется третий до завершения второго). Короче говоря, они выполняются последовательно.

Во втором фрагменте вы запускаете все три вызова службы, но затем не продолжайте в коде до тех пор, пока все три не будут выполнены. Короче говоря, все они выполняются параллельно.

Если второй/третий вызовы не могут быть начались, пока они не получили результат предыдущей операции, тогда вам нужно будет сделать что-то вроде первого фрагмента, чтобы заставить его работать. Если вызовы службы не зависят друг от друга вообще, тогда вы хотите, чтобы они выполнялись параллельно по соображениям производительности.

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

var Data = await Task.WhenAll(WebService1.Call(), 
    WebService2.Call(), 
    WebService3.Call()); 
+0

Не останавливается ли код в ожидании Task1 до завершения Task1, а затем остановится в Task2 и дождитесь Task2 и т. Д.? –

+1

@EricB, я считаю, что только основной поток останавливается. – gdoron

+1

Поток с 'await' остановится, но две задачи будут продолжать продвигаться вперед. – dasblinkenlight

1

Servy posted очень хороший ответ, но здесь это визуальное описание с помощью Task с, чтобы помочь показать, что проблема есть. Этот код не будет функционировать так же, как ваш (он не выполняет все компоненты контекста синхронизации, такие как возврат управления насосом сообщений), но это очень хорошо иллюстрирует проблему.

Ваш код делает что-то вроде этого

var fooTask = Task.Factory.StartNew(Foo); 
fooTask.Wait(); 
var fooResult = fooTask.Result; 

var barTask = Task.Factory.StartNew(Bar); 
barTask.Wait(); 
var barResult = barTask.Result; 

var bazTask = Task.Factory.StartNew(Baz); 
bazTask.Wait(); 
var bazResult = bazTask.Result; 

и исправленный код делает что-то вроде этого

var fooTask = Task.Factory.StartNew(Foo); 
var barTask = Task.Factory.StartNew(Bar); 
var bazTask = Task.Factory.StartNew(Baz); 

fooTask.Wait(); 
var fooResult = fooTask.Result; 
barTask.Wait(); 
var barResult = barTask.Result; 
bazTask.Wait(); 
var bazResult = bazTask.Result; 

Вы можете видеть, что все 3 задачи выполняются во время ожидания первого результата в вернитесь туда, где в первом примере вторая задача не запускается до завершения первой задачи.

38

Правильный ответ Servy; немного расшириться. В чем разница между:

Eat(await cook.MakeSaladAsync()); 
Eat(await cook.MakeSoupAsync()); 
Eat(await cook.MakeSandwichAsync()); 

и

Task<Salad> t1 = cook.MakeSaladAsync(); 
Task<Soup> t2 = cook.MakeSoupAsync(); 
Task<Sandwich> t3 = cook.MakeSandwichAsync(); 
Eat(await t1); 
Eat(await t2); 
Eat(await t3); 

?

Первый:

  • Кук, пожалуйста, сделайте мне салат
  • В ожидании салата, у вас есть немного свободного времени, чтобы почистить кошку. Когда вы закончите это, о, смотрите, салат сделан.Если повар закончил салат, пока вы чистили кошку, они не начали делать суп, потому что вы еще не просили его еще.
  • Съешьте салат. Повар теперь бездействует, пока вы едите.
  • Повар, пожалуйста, сделайте мне суп.
  • В ожидании супа у вас есть свободное время, чтобы очистить аквариум. Когда вы закончите это, о, смотрите, суп сделан. Если повар заканчивает суп, когда вы очищаете аквариум, они не начинаются с бутерброда, потому что вы еще не просили его.
  • Ешьте суп. Повар теперь бездействует, пока вы едите.
  • Повар, пожалуйста, сделайте мне сэндвич.
  • Опять же, найдите что-нибудь еще, пока вы ждете.
  • Съешь бутерброд.

Ваша вторая программа эквивалентна:

  • Кук, пожалуйста, сделайте мне салат
  • Кук, пожалуйста, сделайте мне какой-нибудь суп.
  • Повар, пожалуйста, сделайте мне сэндвич.
  • Выполнен ли салат? Если нет, ожидая салата, у вас есть свободное время, чтобы почистить кошку. Если повар закончил салат, пока вы чистили кошку, они начали делать суп.
  • Съешьте салат. Повар все еще может работать на супе и сэндвиче во время еды.
  • Суп сделан? ...

Вы видите разницу? В своей первоначальной программе вы не говорите повару, чтобы начать следующий курс, пока вы не закончите , едя первый курс. В вашей второй программе вы запросите все три курса вперед и съедите их - по порядку - по мере их поступления. Вторая программа лучше использует время повара, потому что повар может «опередить» вас.

+1

Я думаю, что важно отметить, что повар может делать несколько блюд одновременно, что, как представляется, явно не указано. Так что в случае 2, когда вы попросите все 3 блюда, повар может приготовить салат, а суп запирается в духовку. поэтому в случае 1 нет смысла просить повара сделать одну вещь за раз, так как они могут делать больше чем одно блюдо за раз. – Letseatlunch

+0

@Letseatlunch: Это хороший момент, но это не обязательно для моего аргумента; важно отметить, что когда вы сначала запрашиваете все три вещи, поставщик услуг может делать их все сразу или один за другим, но по крайней мере они знают *, чтобы делать все три *. Если вы не запрашиваете второй, пока не будет выполнено первое, вы, возможно, недоиспользуете ресурс. –

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