首页 > 解决方案 > 在这种情况下,异步是否提供了性能改进?(.Net Core 2.1 Web API)

问题描述

我有一个 .Net Core 2.1 Web API。我的许多控制器操作都不是异步的,我正在考虑让它们异步。但我想知道它是否至少在简单的场景中会有任何性能改进?以一个非常基本的动作为例:

// GET: api/People/5
[HttpGet("id")]
public Person GetPerson([FromRoute] int id)
{
    return _context.People.FirstOrDefault(p => p.Id == id);
}

如果我将其转换为:

// GET: api/People/5
[HttpGet("id")]
public async Task<Person> GetPerson([FromRoute] int id)
{
    return await _context.People.FirstOrDefaultAsync(p => p.Id == id);
}

当然,至少在这个非常基本的例子中,这两个动作会在同一时间段内执行?我的意思是,即使有两个同时调用非异步操作方法,IIS/Kestrel 也会承担创建它的第二个实例以允许两者同时执行的任务?我的意思是,这不是因为一个客户端调用了这个 API 端点,现在所有其他客户端必须等待它完成才能提供服务,因为它不是异步的?

标签: async-awaitasp.net-core-webapiasp.net-core-2.1

解决方案


异步不是关于性能,而是关于可伸缩性。实际上,您实际上是在牺牲原始性能以获得更好的可伸缩性,因为异步至少有一些开销,这将导致一切的性能至少稍微降低一些,即使它只是几纳秒的问题。

Async 有一个目的:允许线程在空闲时返回到池中。然而,无论它是否处于等待状态,同步都会保留在线程上。但是,不能保证线程会被释放,甚至不能保证它可以被释放,因为异步方法通常需要执行同步工作(即 CPU-bound),这需要主动使用线程。

当你被猛烈抨击时,你诚实地看到真正的好处。在您的 Web 服务将成为运行所有同步的线程饥饿的情况下,您可能能够通过异步推送更多请求。影响的程度不仅取决于服务器的猛烈程度,还取决于所有组成操作的猛烈程度。以您的单个FirstOrDefault查询示例为例,在大多数情况下,它可能几乎是瞬时的。但是,如果您的数据库实例受到一些大型/复杂查询的影响,或者只是因为太多查询而无法及时处理,那么突然之间,这个简单的过程可能会开始比平时花费更长的时间。然后可能会开始备份队列中的请求。异步为您提供了一些喘息的空间。

总而言之,使用异步的性能权衡通常非常小,在您需要它的时候,它可以成为救命稻草。您总是可以等待,分析,先查看是否有问题,然后返回并使事情异步,但事后返回并将非异步的事情重新调整为异步是一件令人头疼的事情。如果有任何异步工作要做(I/O),那么从一开始就使用异步几乎总是更好。

此外,FWIW,虽然并非总是如此,尤其是 EF Core,但实际上并没有“同步”访问。非异步方法只是阻塞异步版本。所以,真的没有什么好借口不继续使用异步版本。


推荐阅读