何时使用"await"关键字

Eri*_*c B 23 c# web-services task-parallel-library async-await .net-4.5

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

var Data1 = await WebService1.Call();
var Data2 = await WebService2.Call();
var Data3 = await WebService3.Call();
Run Code Online (Sandbox Code Playgroud)

在代码审查期间,有人说我应该将其更改为:

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

var Data1 = await Task1;
var Data2 = await Task2;
var Data3 = await Task3;
Run Code Online (Sandbox Code Playgroud)

为什么?有什么不同?

Eri*_*ert 38

Servy的回答是正确的; 稍微扩大一点.有什么区别:

Eat(await cook.MakeSaladAsync());
Eat(await cook.MakeSoupAsync());
Eat(await cook.MakeSandwichAsync());
Run Code Online (Sandbox Code Playgroud)

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

首先是:

  • 库克,请给我一份沙拉
  • 在等沙拉时,你有空闲时间刷猫.当你做完了,哦,看,沙拉完成了. 如果厨师在你刷猫的时候吃完沙拉,他们就没有开始制作汤,因为你还没有要求它.
  • 吃沙拉. 你吃饭的时候,厨师现在闲着.
  • 库克,请给我一些汤.
  • 在等待汤的同时,你有一些空闲时间来清理鱼缸.当你完成它,哦,看,汤完成了.如果厨师在你清洗鱼缸时完成了汤,他们就不会开始吃三明治,因为你还没有要求它.
  • 吃汤.你吃饭的时候,厨师现在闲着.
  • 库克,请给我一个三明治.
  • 再次,在等待时找到别的事情.
  • 吃三明治.

你的第二个计划相当于:

  • 库克,请给我一份沙拉
  • 库克,请给我一些汤.
  • 库克,请给我一个三明治.
  • 沙拉做完了吗?如果没有,在等沙拉时,你有空闲时间刷猫.如果厨师在你刷猫的时候吃完沙拉,他们就开始做汤了.
  • 吃沙拉. 在你吃饭的时候,厨师仍然可以做汤和三明治.
  • 汤做完了吗?...

你看到了区别?在原来的计划,你不告诉厨师,开始下一疗程,直到你完成的第一道菜.在你的第二个课程中,你提前申请所有三个课程,并按顺序吃它们 - 因为它们可用.第二个程序更好地利用了厨师的时间,因为厨师可以"领先"你.


Ser*_*rvy 33

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

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

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

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

var Data = await Task.WhenAll(WebService1.Call(), 
    WebService2.Call(), 
    WebService3.Call());
Run Code Online (Sandbox Code Playgroud)

  • @EricB:`WhenAll`会更有效率,而且我个人认为它更好地捕获了所需的语义("等待所有这些完成"而不是"等待第一个,然后是第二个,然后是第三个"). (2认同)