何时使用“await”关键字

时间:2013-05-30 19:14:14

标签: c# web-services task-parallel-library .net-4.5 async-await

我正在写一个网页,它会调用一些网络服务。电话看起来像这样:

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;

为什么呢?有什么区别?

3 个答案:

答案 0 :(得分: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 :(得分:33)

在第一个代码段中,您甚至不会启动第二个服务调用,直到第一个服务调用完成(同样在第二个服务调用完成之前不会启动第三个服务调用)。简而言之,它们是按顺序执行的。

在第二个片段中,您启动所有三个服务调用,但是在完成所有这三个服务调用之前不要继续执行。简而言之,它们都是并行执行的。

如果第二次/第三次调用无法启动,直到他们获得上一次操作的结果,那么您需要执行类似第一个代码段的操作才能使其正常工作。如果服务调用完全不依赖于彼此,那么出于性能原因,您希望它们并行执行。

如果出于某种原因,您真的不喜欢使用额外的局部变量,那么还有其他方法可以使用备用语法并行执行任务。一种可能与您的第二种选择相同的替代方案是:

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

答案 2 :(得分: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个任务在等待第一个结果返回时正在运行,在第一个示例中,第二个任务在第一个任务完成之前不会启动。