是否有理由让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;
}
我不明白这种行为。
我实际上并不太关心我的具体情况,我想理解为什么,在基于参考的观点上,这应该发生。我目前的理解是,一旦Task被调用就会启动,但它应该异步执行。那么,为什么主线程会等待创建异步任务呢?
先感谢您!
只需创建它们需要600ms-1800ms
最有可能的是,你的数据库密集型工作正在同步完成一些工作;或许打开连接,也许发出命令 - 我们不可能知道。如果您向我们展示了FryEggs
的确切功能,并告诉我们您正在使用哪个ADO.NET提供商,我们可能会进一步提供建议。请注意,某些ADO.NET提供程序对其*Async
实现使用async-over-sync,这意味着它们实际上只是同步。因此,确切的ADO.NET提供程序和版本非常重要。
当异步方法返回一个Task时,它用于继续该方法。该方法的开头仍然立即执行。
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);
}