异步过度同步表现更好?

时间:2016-11-12 00:27:37

标签: c# asynchronous

我很清楚不建议使用异步过度同步。原因是有道理的。

在设置Web API控制器时,我使用和不使用async over sync进行了测试。我设置了一个表格,其中包含来自我的服务的数据和一个按钮,该按钮可以同时更新每个项目以及对服务的单独请求。令我惊讶的是,没有异步过度同步的设置似乎一次处理五个批次;异步过度同步快速,单独地完成了更新:整个批次的完成时间比一批所花费的时间多一点。

总之,对于Web API控制器:

  • async over sync似乎并行运行所有请求。
  • 普通代码一次并行运行最多五(5)个请求。

鉴于重复的Q& As和集体文献,我对使用异步过同步性能的结论必定存在缺陷。 你能解释一下我看到的行为吗?我怀疑答案在于我如何设置我的本地系统,但我很欣赏那些比我更有知识的人。

“Async over sync”就是这样的:

public async Task SyncAsync()
{
    return new Task(() => Thread.Sleep(2000));
}

根据sources

  

因为你只是将工作从一个ThreadPool线程卸载到另一个线程,所以毫无意义。

所以我不妨写下这个:

public void Sync()
{
    Thread.Sleep(2000);
}

2 个答案:

答案 0 :(得分:1)

更新1 您试图说Async并行执行所有请求,但在这种情况下,它会将工作转移到线程池到线程。

  1. 我也在下面解释了它,但是理解你的所有请求都是关于某些API调用或一些I / O调用,在这种情况下你提供更多请求但是在Sync的情况下你只有服务器5请求,因为当前线程是块。
  2. 在上面我假设线程池的容量为5。

    因此当6个请求到来时,它将等待同步。在异步的情况下,因为I / O线程池中的所有5个请求都有自由线程到服务器6的请求。这样可以提高应用程序的吞吐量。

    此外,您必须确保Task不是线程。任务和线程是不同的。

    我在这里和我在开发过程中测量的最好的一般情况一样。

    1. 异步/等待不是改善性能的银弹。你必须正确实现它。当执行的任务是基于I / O的,如某些数据库调用,Web服务调用或文件系统调用时,应使用Async / Await。在这种情况下,它给予了益处,因为I / O操作不需要线程,并且它不必要阻塞一个线程。

    2. 如果您在异步/等待中执行CPU密集型任务,则它不会为您带来性能优势。

    3. 此外,您还必须在各种负载下测试您的应用程序。就像你已经托管你的应用程序IIS和本地你正在测试调试然后总是更少的资源消耗,然后你觉得同步是好的,因为他们获得免费资源,但当你增加负载(像一次性的许多用户)那么它在Sync中创建瓶颈,好像它找不到空闲线程,即使在IIS线程内等待I / O完成,请求也不会服务。

    4. https://msdn.microsoft.com/en-us/magazine/hh456402.aspx

      https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

答案 1 :(得分:0)

我从你与dotnetstep的讨论中看到你正在尝试组建一个MVC应用程序,但我认为我不需要看到它来回答你的问题。 Dotnetstep的回答是正确的,但我想详细说明你在评论中提出的一些其他问题。

  

我想了解5的线程池来自哪里

这只是dotnetstep选择的任意数字。该数字将来自web.config文件的processModel> maxWorkerThreads部分。这是ASP.NET可以处理的并发执行请求的数量。此数字鼓励开发人员最小化请求的执行时间。例如,如果此数字为5,则ASP.NET可以同时处理5个请求。但是如果请求依赖于其他一些I / O操作会发生什么?如果您需要拨打网络服务怎么办?

在“同步”方案中,当Request1进入并调用Web服务时,Request1的线程将一直等到来自Web服务的响应已到达。 Request2,Request3,Request4和Request5也是如此。当Request6进入服务器时,将返回HTTP 503响应(“服务不可用”),因为所有5个线程都在等待Web服务响应。此时,在其中一个请求完成并且线程已返回到线程池之前,您的服务器无法再处理任何请求。

在“异步”方案中,Request1进入并调用Web服务,Request1的线程不会等待来自Web服务的响应,但会立即返回到线程池。 Request2,Request3,Request4和Request5也是如此。当Request6进入时,服务器拥有全部5个线程(假设没有返回任何Web服务调用)来处理请求。这是“异步”的好处,因为它不会阻止你的线程。这样您就可以同时处理更多请求。

您可以清楚地看到这一切都取决于您在请求中所做的事情。如果请求正在执行需要纯CPU注意力(CPU密集型工作)的工作,那么async将无法帮助您,因为无论是异步还是同步,CPU都被占用而且不能执行任何其他操作。因此,您的请求将不得不等待。注意你在异步操作中所做的事情。如果您为测试目的所做的一切都是:

Thread.Sleep(2000);

然后你的线程仍然被占用,无论是同步还是异步(线程只有一种方式可以睡眠,它无法休眠并通过返回线程池同时工作)。

执行非常异步的操作,例如,从大文件中读取,您将看到真正的好处或异步vs同步。

  

以及为什么它与正在卸载的线程池不同。

执行I / O请求的线程与提供这些ASP请求的线程不同。在某些情况下,它甚至不是同一台机器,例如,如果您调用另一台机器上的Web服务或从其他机器上的文件中读取...您就明白了。

更新1:

当你有这样的代码时,可能有益的一点信息:

return new Task(() => Thread.Sleep(2000));

如果Request1的线程正在处理上述代码,它将被卸载到另一个线程(Request1,Request2,Request3或Request4)。那个其他线程会睡觉。所以基本上它会花费你更多,因为你改变了背景,但你没有得到任何回报。因此,由于此处的上下文切换,您的异步版本将比同步版本慢。

此外,请记住,无论是同步还是异步,客户端必须等待的时间几乎相同(不会更快地完成)。唯一的好处是您的服务器可以处理更多请求。这项工作仍然需要以某种方式完成(同步或异步)。