我可以 运行 将代码同步为异步以获得性能吗?

Can I run sync code as async to gain performance?

我阅读了很多讨论 Async/Await 在 C# 中的用法的论坛、教程和博客。我读得越多,它就越混乱。 另外,人们主要谈论在同步方法中调用异步的东西,而不是在异步方法中调用同步的东西。

由于我是一名初级开发人员并且没有太多异步编程经验,所以我将 post 在这里提出一个新问题并希望得到一些启发。

考虑一下:

我有一个 Web API 端点,可以进行一些计算和模型构建以及 return 一些数据。

public async Task<JsonResult> GetData()
    {
        Task<Data> stuff1Task = CalculateStuff1();
        Task<Data> stuff2Task = CalculateStuff2();
        Task<Data> stuff3Task = CalculateStuff3();

        return Json(
            new
            {
                stuff1 = await stuff1Task,
                stuff2 = await stuff2Task,
                stuff3 = await stuff3Task
            }, JsonRequestBehavior.AllowGet
        );
    }

    private async Task<Data> CalculateStuff1()
    {
        return await SomeAsyncCalculation();
    }

    private async Task<Data> CalculateStuff2()
    {
        return SomeSyncCalculation();
    }

    private async Task<Data> CalculateStuff3()
    {
        Task<Data> dataTask1 = SomeAsyncCalculation();
        Task<Data> dataTask2 = AnotherAsyncCalculation();
        
        Data data1 = await dataTask1;
        Data data2 = await dataTask2;

        Data combindedData = SyncMethodToCombineData(data1, data2);

        return combindedData;
    }

为什么我考虑混合异步和同步代码是为了获得更好的性能。 在这种情况下,假设 SomeAsyncCalculation()SomeSyncCalculation()AnotherAsyncCalculation() 是相当昂贵的方法。我的目标是让方法 运行 稍微并行一些以获得更快的响应时间。

我知道最好“一直异步”,但说实话,重建项目的一半并不总是优先事项或可能性。 此外,我可能与不支持异步操作的其他系统进行了一些集成。

我收到的关于 CalculateStuff2() 的警告增加了混乱。 :

this async method lacks 'await' operators and will run synchronously

据我了解,“async”关键字仅适用于包装方法并允许我使用 await 关键字。它还允许我只 return 数据,我不需要管理任务 returning 结果。它还处理异常。 Task<TResult> return 类型使方法在不同的线程上执行(尽管不能保证它会在不同的线程上执行)。

结论性问题:

1. 不使用 await (CalculateStuff2()) 运行 的异步方法是否会在它自己的线程上同步(如果它 运行s 在另一个线程上,因为它是 Task) 还是会在 API 调用的主线程中 运行,无论如何总是阻塞它?

2。在没有 await 的情况下使用 async 只是为了有一个开箱即用的包装好的任务方法是不好的做法吗?

您不需要 async 同步方法。 async 生成状态机,这是一种冗余,以防您不需要 await

考虑这个稍微优化的示例。

public async Task<JsonResult> GetData()
{
    Task<Data> stuff1Task = CalculateStuff1();
    Task<Data> stuff3Task = CalculateStuff3();
    Data stuff2data = CalculateStuff2(); // run sync method after launching async ones

    return Json(new
           {
               stuff1 = await stuff1Task,
               stuff2 = stuff2data,
               stuff3 = await stuff3Task
           }, JsonRequestBehavior.AllowGet);
}

private Task<Data> CalculateStuff1() // optimized
{
    return SomeAsyncCalculation();
}

private Data CalculateStuff2()
{
    return SomeSyncCalculation();
}

private async Task<Data> CalculateStuff3()
{
    //use combinator to simplify the code
    Data[] data = await Task.WhenAll(SomeAsyncCalculation(), AnotherAsyncCalculation());

    Data combindedData = SyncMethodToCombineData(data[0], data[1]);

    return combindedData;
}

还要考虑区分 CPU 绑定和 IO 绑定操作,请查看 this article。有不同的异步方法,具体取决于您要启动的具体内容。

直接回答

  1. Will the async method that does not use await (CalculateStuff2()) run synchronously on it's own thread (if it runs on another thread because it is a Task) or will it run in the main thread of the API call, and always block it no matter what?

是的,它将 运行 在调用者线程上同步。如果你想 运行 在它自己的线程上使用一些同步方法,使用 Task.Run():

Task<Data> stuff2Task = Task.Run(() => CalculateStuff2());

然后await它。

  1. Is it bad practice to use async without await just to have a nicely wrapped task method out of the box?

是的,这是不好的做法。冗余状态机产生的开销在这种情况下毫无价值。