.NET异步任务创建时间过长

时间:2019-04-24 21:45:09

标签: c# asynchronous async-await task

创建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;
}

我不了解这种行为。

实际上,我不太关心自己的具体情况,我想从基于参考的角度理解为什么。我目前的理解是,任务一被调用就立即启动,但它应该异步启动。那么,为什么主线程会等待这么多时间来创建异步任务?

提前谢谢!

2 个答案:

答案 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);
}