我正在写一个网页,它会调用一些网络服务。电话看起来像这样:
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 :(得分: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个任务在等待第一个结果返回时正在运行,在第一个示例中,第二个任务在第一个任务完成之前不会启动。