创建async Task<> foo()
需要花费一些时间吗? (刚创建时,等待它甚至需要花更多的时间)。
在对Web服务性能进行性能分析时,我注意到仅创建任务需要花费大量时间(数百毫秒)。
//DB intensive async tasks, just creating them all takes 600ms-1800ms
Task<Egg> eggTask = FryEggs(2);
Task<Bacon> baconTask = FryBacon(3);
Task<Toast> toastTask = ToastBread(2);
//actually awaiting them takes some hundreds of milliseconds more...
await Task.WhenAll(eggTask, baconTask, toastTask);
有问题的函数在第一次等待它们内部之前不会进行繁重的同步工作,因此我找不到令人信服的理由让它们以这种方式工作。这样的事情:
async Task<Object> FryEggs(){
VeryLightweightSynchronousWork();
await BottleneckingIOWork();
}
我尝试使用Task.Factory.StartNew
,它确实立即返回:
async Task<Object> FryEggs(){
var res = await Task.Factory.StartNew(async => {
VeryLightweightSynchronousWork();
return await BottleneckingIOWork();
}
return await res;
}
我不了解这种行为。
实际上,我不太关心自己的具体情况,我想从基于参考的角度理解为什么。我目前的理解是,任务一被调用就立即启动,但它应该异步启动。那么,为什么主线程会等待这么多时间来创建异步任务?
提前谢谢!
答案 0 :(得分:1)
仅创建它们就要花费600ms-1800ms
您的数据库密集型工作很可能是同步完成某些工作;也许打开连接,也许发出命令-我们可能不知道。如果您确实{strong>确切地向我们展示了FryEggs
的功能,并且告诉我们您使用的是哪个ADO.NET提供程序,我们也许可以提供进一步的建议。请注意,某些ADO.NET提供程序在其*Async
实现中使用了异步同步,这意味着它们实际上只是同步的。因此,确切的ADO.NET提供程序和版本非常重要。
答案 1 :(得分:0)
异步方法返回任务时,表示该方法的继续。该方法的开头仍会立即执行。
async Task FryEggs(int count)
{
//Anything here will cause FryEggs to block the caller until it is completed
await DoSomethingAsync();
//Anything down here is the "continuation" which will execute after the task is returned
}
如果您希望立即返回未完成的任务,则可以在开始时添加一个等待,例如使用Task.Yield
:
async Task FryEggs(int count)
{
await Task.Yield();
//Now this is part of the continuation too. It won't run until after the Task is returned.
await DoSomethingAsync();
}
我不确定这是否一定是您的最佳方案,但这是一个选择。
如果FryEggs包含CPU限制的工作,并且您希望通过并行性进行改进,则可能需要在单独的线程上运行它:
async Task FryEggs(int count)
{
await Task.Run( () =>
{
//Do something super-expensive and synchronous
});
await DoSomethingAsync();
}
这样一开始就有一个等待,因此控制权立即得到了释放。昂贵的东西放在线程池中自己的线程上。逻辑可以通过closures访问相同的局部变量;只要您等待Task.Run()
通话,这是安全的。
async Task FryEggs(int count)
{
string[] results; //This local variable will be closed over
await Task.Run( () =>
{
//Do something super-expensive and synchronous
results = GetResults();
});
await DoSomethingAsync(results);
}