ASP.NET Core同步和异步控制器操作之间没有太大区别 [英] Not much difference between ASP.NET Core sync and async controller actions

查看:955
本文介绍了ASP.NET Core同步和异步控制器操作之间没有太大区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在控制器中编写了几种操作方法,以测试ASP.NET核心中的 sync async 控制器操作之间的区别:

I wrote a couple of action methods in a controller to test the difference between sync and async controller actions in ASP.NET core:

[Route("api/syncvasync")]
public class SyncVAsyncController : Controller
{
    [HttpGet("sync")]
    public IActionResult SyncGet()
    {
        Task.Delay(200).Wait();

        return Ok(new { });
    }

    [HttpGet("async")]
    public async Task<IActionResult> AsyncGet()
    {
        await Task.Delay(200);

        return Ok(new { });
    }
}

然后我对同步端点进行了负载测试:

I then load tested the sync end point:

...,然后是异步端点:

... followed by the async end point:

如果将延迟增加到1000毫秒,则结果如下

Here's the results if I increase the delay to 1000 ms

如您所见,每秒请求并没有太大差异-我希望异步端点每秒处理更多的请求.我想念什么吗?

As you can see there is not much difference in the requests per second - I expected the async end point to handle more requests per second. Am I missing something?

推荐答案

是的,您丢失了异步与速度无关的事实,异步与每秒请求的概念仅相关一点.

Yes, you are missing the fact that async is not about speed, and is only slightly related to the concept of requests per second.

异步只做一件事.如果正在等待任务,并且该任务不涉及CPU限制的工作,结果该线程变为空闲状态,那么该线程潜在地可以被释放以返回到池中进行操作其他工作.

Async does one thing and only one thing. If a task is being awaited, and that task does not involve CPU-bound work, and as a result, the thread becomes idle, then, that thread potentially could be released to return to the pool to do other work.

就是这样.异步地说.异步的目的是更有效地利用资源.如果您的线程可能被捆绑在一起,只是坐在那里敲击他们的脚趾,等待一些I/O操作完成,那么他们可以承担其他工作.这导致您应该内部化两个非常重要的想法:

That's it. Async in a nutshell. The point of async is to utilize resources more efficiently. In situations where you might have had threads tied up, just sitting there tapping their toes, waiting for some I/O operation to complete, they can instead be tasked with other work. This results in two very important ideas you should internalize:

  1. 异步!=更快.实际上,异步是 slower .异步操作涉及开销:上下文切换,数据在堆上和堆外的混洗等.这加起来需要额外的处理时间.即使在某些情况下我们只说微秒,异步也会总是比等效的同步过程慢.时期.句号.

  1. Async != faster. In fact, async is slower. There's overhead involved in an asynchronous operation: context switching, data being shuffled on and off the heap, etc. That adds up to additional processing time. Even if we're only talking microseconds in some cases, async will always be slower than an equivalent sync process. Period. Full stop.

仅当服务器处于负载状态时,异步才会购买任何东西.仅在服务器受到压力时,异步才会为其提供一些急需的呼吸空间,而同步可能会使它屈服.一切都与规模有关.如果您的服务器仅处理少量请求,那么您极有可能永远不会看到同步方面的差异,并且就像我说的那样,具有讽刺意味的是,由于涉及的开销,您最终可能会使用更多资源.

Async only buys you anything if your server is at load. It's only at times when your server is stressed that async will give it some much needed breathing room, whereas sync might bring it to its knees. It's all about scale. If your server is only fielding a minuscule amount of requests, you very likely will never see a difference over sync, and like I said, you may end up using more resources, ironically, because of the overhead involved.

这并不意味着您不应该使用异步.即使您的应用程序今天不流行,也并不意味着它不会晚些,并且那时重新调整所有代码以支持异步将是一场噩梦.异步的性能成本通常可以忽略不计,并且如果您最终需要它,它将节省很多时间.

That doesn't mean you shouldn't use async. Even if your app isn't popular today, it doesn't mean it won't be later, and rejiggering all your code at that point to support async will be a nightmare. The performance cost of async is usually negligible, and if you do end up needing it, it'll be a life-saver.

更新

关于使异步的性能成本可忽略不计,有一些有用的提示,在大多数C#异步讨论中,这些提示并不明显或说明得很好.

In the regard of keeping the performance cost of async negligible, there's a few helpful tips, that aren't obvious or really spelled out that well in most discussions of async in C#.

  • 尽可能使用ConfigureAwait(false).

await DoSomethingAsync().ConfigureAwait(false);

几乎每个异步方法调用都应跟随此操作,除了一些特定的例外. ConfigureAwait(false)告诉运行时您不需要在异步操作期间保留的同步上下文.默认情况下,当您等待异步操作时,将创建一个对象来保留线程开关之间的线程本地.这占用了处理异步操作所涉及的大部分处理时间,并且在许多情况下完全没有必要.真正重要的唯一地方是诸如操作方法,UI线程等之类的地方,在这些地方有与需要保留的线程相关的信息.您只需要保留一次此上下文,例如,只要您的操作方法等待一个同步上下文完整的异步操作,该操作本身就可以执行其他不保留同步上下文的异步操作.因此,在操作方法之类的事情中,应将await的使用限制在最小范围内,而应尝试将多个异步操作分组为该操作方法可以调用的单个异步方法.这将减少使用异步所涉及的开销.值得注意的是,这仅是ASP.NET MVC中操作的关注点. ASP.NET Core利用依赖项注入模型而不是静态模型,因此无需担心线程本地性.在其他情况下,您可以 在ASP.NET Core操作中使用ConfigureAwait(false),但不能在ASP.NET MVC中使用.实际上,如果尝试尝试,则会出现运行时错误.

Pretty much every asynchronous method call should be followed by this except for a few specific exceptions. ConfigureAwait(false) tells the runtime that you don't need the synchronization context preserved during the async operation. By default when you await an async operation an object is created to preserve thread locals between thread switches. This takes up a large part of the processing time involved in handling an async operation, and in many cases is completely unnecessary. The only places it really matters is in things like action methods, UI threads, etc - places where there's information tied to the thread that needs to be preserved. You only need to preserve this context once, so as long as your action method, for example, awaits an async operation with the synchronization context intact, that operation itself can perform other async operations where the synchronization context is not preserved. Because of this, you should confine uses of await to a minimum in things like action methods, and instead try to group multiple async operations into a single async method that that action method can call. This will reduce the overhead involved in using async. It's worth noting that this is only a concern for actions in ASP.NET MVC. ASP.NET Core utilizes a dependency injection model instead of statics, so there are no thread locals to be concerned about. In others you can use ConfigureAwait(false) in an ASP.NET Core action, but not in ASP.NET MVC. In fact, if you try, you'll get a runtime error.

应尽可能减少需要保留的本地数量.您在调用await之前初始化的变量将添加到堆中,并在任务完成后弹出.您声明的越多,堆上的内容就越多.特别是大型对象图可能会在这里造成破坏,因为在堆上和下移动这是大量的信息.有时这是不可避免的,但要记住这一点.

As much as possible, you should reduce the amount of locals that need to be preserved. Variables that you initialize before calling await are added to the heap and the popped back off once the task has completed. The more you've declared, the more that goes onto the heap. In particular large object graphs can wreck havoc here, because that's a ton of information to move on and off the heap. Sometimes this is unavoidable, but it's something to be mindful of.

在可能的情况下,淘汰async/await关键字.请考虑以下示例:

When possible, elide the async/await keywords. Consider the following for example:

public async Task DoSomethingAsync()
{
    await DoSomethingElseAsync();
}

在这里,DoSomethingElseAsync返回一个等待并已展开的Task.然后,创建一个新的Task以从DoSometingAsync返回.但是,相反,您将方法编写为:

Here, DoSomethingElseAsync returns a Task that is awaited and unwrapped. Then, a new Task is created to return from DoSometingAsync. However, if instead, you wrote the method as:

public Task DoSomethingAsync()
{
    return DoSomethingElseAsync();
}

DoSomethingElseAsync返回的TaskDoSomethingAsync直接返回.这样可以减少大量的开销.

The Task returned by DoSomethingElseAsync is returned directly by DoSomethingAsync. This reduces a significant amount of overhead.

这篇关于ASP.NET Core同步和异步控制器操作之间没有太大区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆