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;

为什么?有什么不同?

4

3 回答 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);

?

第一个是:

  • 厨师,请给我做沙拉
  • 在等待沙拉的时候,你有一些空闲时间给猫刷牙。完成后,哦,看,沙拉就做好了。 如果厨师在你刷猫的时候完成了沙拉,他们并没有开始做汤,因为你还没有要求它
  • 吃沙拉。 当你吃饭时,厨师现在处于空闲状态。
  • 厨师,请给我煮点汤。
  • 在等待汤时,您有一些空闲时间来清洁鱼缸。做完之后,哦,看,汤就做好了。如果厨师在你清理鱼缸时煮完汤,他们不会从三明治开始,因为你还没有要求。
  • 吃汤。当你吃饭时,厨师现在处于空闲状态。
  • 厨师,请给我做个三明治。
  • 再一次,在你等待的时候找点别的事情做。
  • 吃三明治。

您的第二个程序相当于:

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

你看出区别了吗?在您的原始程序中,您不会告诉厨师在您吃完第一道菜之前开始下一道菜。在您的第二个程序中,您预先要求所有三道菜,并在它们可用时按顺序食用它们。第二个程序更好地利用了厨师的时间,因为厨师可以“领先”你。

于 2013-05-30T19:27:00.450 回答
33

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

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

如果在获得上一个操作的结果之前无法启动第二个/第三个调用,那么您需要执行类似于第一个片段的操作才能使其正常工作。如果服务调用根本不相互依赖,那么出于性能原因,您希望它们并行执行。

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

var Data = await Task.WhenAll(WebService1.Call(), 
    WebService2.Call(), 
    WebService3.Call());
于 2013-05-30T19:15:34.883 回答
1

Servy 发布了一个非常好的答案,但这里是一个使用Tasks 的视觉描述来帮助显示问题所在。此代码的功能与您的不同(它不会执行所有同步上下文的操作,例如将控制权交还给消息泵),但它很好地说明了问题。

你的代码正在做这样的事情

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 个任务都在运行,同时等待第一个结果返回,在第一个示例中,第二个任务直到第一个任务完成后才开始。

于 2013-05-30T19:25:14.770 回答