【发布时间】:2018-11-18 15:52:46
【问题描述】:
我有一个 WCF 服务和一个我不想更改的合同(尽管如果有必要我将不得不更改):
[OperationContract]
PackageDto GetPackage(int id);
还有服务:
public PackageDto GetPackage(int id)
{
var foo1 = FooData.GetFoo1(id);
var foo2 = FooData.GetFoo2(id);
var foo3 = FooData.GetFoo3(id);
var bar1 = FooLogic.CalculateBar(foo1, foo2, foo3);
var bar2 = FooLogic.CalculateBar(foo1, foo2);
return new PackageDto(){ Bar1 = bar1, Bar2 = bar2 };
}
我正在进行 3 次独立的数据调用来获取 foos。我觉得我可以同时运行 foo 操作并等待所有结果。然后照常进行。我在考虑 async/await,但我不确定这是否是可行的方法。毕竟,我们首先讨论的是并行运行,而不是异步。 Foo 方法不是“真正的”异步方法,它们确实阻塞了线程,所以这就是我感到困惑的原因。这是我的想法:
public PackageDto GetPackage(int id)
{
return Task.Run(async () =>
{
var tasks = new List<Task>();
var foo1Task = Task.Run(() => FooData.GetFoo1(id));
var foo2Task = Task.Run(() => FooData.GetFoo2(id));
var foo3Task = Task.Run(() => FooData.GetFoo3(id));
tasks.Add(foo1Task);
tasks.Add(foo2Task);
tasks.Add(foo3Task);
await Task.WhenAll(tasks);
var bar1 = FooLogic.CalculateBar(foo1Task.Result, foo2Task.Result, foo3Task.Result);
var bar2 = FooLogic.CalculateBar(foo1Task.Result, foo2Task.Result);
return new PackageDto(){ Bar1 = bar1, Bar2 = bar2 };
}).Result;
}
你会怎么做?
【问题讨论】:
-
这取决于调用
GetFoo的性质。如果FooData.GetFoo1..3都是 IO 绑定的,那么可以将签名转换为返回Task或Task<T>的异步签名。如果它们都受 CPU 限制,那么您可以使用并发方法,例如Parallel.Foreach。无论哪种方式,将东西包装在Task.Run中并使用阻塞调用.Result都不是正确的选择。 -
您的一般模式看起来不错,但我会将
GetPackage转换为实际的Task<PackageDto>,并将其标记为async。那你就不需要你的外层Task.Run了,你可以await它。
标签: c# .net async-await task-parallel-library multitasking