简体   繁体   English

ASP.NET Core和Kestrel线程池中的异步处理

[英]Async processing in asp.net core and kestrel thread pool

I'm new to ASP.NET Core and to C# in general coming from the Java world and I'm a bit confused about how async/await keywords work: This article explains that if a method is marked as async it means that 我是Java世界中的ASP.NET Core和C#的新手,对于async / await关键字的工作方式我有些困惑: 本文解释说,如果将方法标记为异步,则意味着

"this method contains control flow that involves awaiting asynchronous operations and will therefore be rewritten by the compiler into continuation passing style to ensure that the asynchronous operations can resume this method at the right spot.” The whole point of async methods it that you stay on the current thread as much as possible “此方法包含涉及等待异步操作的控制流,因此将被编译器重写为连续传递样式,以确保异步操作可以在正确的位置恢复该方法。” 异步方法的全部要点当前线程

So I thought that it is a very cool way of passing control between an async method and its caller running on the same thread implemented on byte-code/virtual machine level. 因此,我认为这是在异步方法和在字节码/虚拟机级别实现的同一线程上运行的调用方之间传递控制的一种非常酷的方法。 I mean I expected that the below code 我的意思是我期望以下代码

public static void Main(string[] args)
{
    int delay = 10;
    var task = doSthAsync(delay);
    System.Console.WriteLine("main 1: " + Thread.CurrentThread.ManagedThreadId);
    // some synchronous processing longer than delay:
    Thread.Sleep(2 * delay)
    System.Console.WriteLine("main 2: " + Thread.CurrentThread.ManagedThreadId);
    task.Wait();
}

public static async Task<String> doSthAsync(int delay)
{
    System.Console.WriteLine("async 1: " + Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(delay);
    System.Console.WriteLine("async 2: " + Thread.CurrentThread.ManagedThreadId);
    return "done";
}

would write 会写

async 1: 1
main 1: 1
main 2: 1
async 2: 1

by passing control from doSthAsync to Main with await keyword and then back to doSthAsync with task.Wait() (and all of this happening on a single thread). 通过从传递控制doSthAsyncMainawait关键字,然后回到doSthAsynctask.Wait()在单个线程和所有这种情况发生的)。

However the code above in fact prints 但是实际上上面的代码可以打印

async 1: 1
main 1: 1
async 2: 4
main 2: 1

which means that it is merely a way to delegate an "async" method to a separate thread if the current thread is busy, which is almost exactly opposite to what the mentioned article states: 这意味着,如果当前线程繁忙,这仅仅是将“异步”方法委派给单独线程的一种方法,这与上述文章所指出的几乎完全相反

The “async” modifier on the method does not mean “this method is automatically scheduled to run on a worker thread asynchronously” 该方法上的“异步”修饰符并不意味着“该方法被自动调度为异步在工作线程上运行”

So since "continuations" of async methods apparently are or at least may be scheduled to run on a different thread, I have some questions that seem fundamental for C# apps scalability: 如此以来的异步方法“延续”显然或者至少可以被安排在不同的线程上运行,我有一些问题,似乎对C#应用程式的基本可扩展性:

Does each async method may run on a newly spawned thread or is the C# runtime maintaining a special thread-pool for this purpose and in the latter case how do I control the size of this pool? 每个异步方法都可以在新生成的线程上运行,还是C#运行时为此目的维护一个特殊的线程池,在后一种情况下,我如何控制该池的大小? UPDATE: thanks to @Servy I now know that in case of CMD-line apps it will be a thread-pool thread, but I still don't know how to control the number of these thread-pool threads). 更新:感谢@Servy,我现在知道在CMD行应用程序中它将是一个线程池线程,但是我仍然不知道如何控制这些线程池线程的数量。

How about in case of ASP.NET Core: how can I control the size of a thread-pool that Kestrel uses to perform async entity framework operations or other (network) I/O? 如果使用ASP.NET Core,该如何处理:如何控制Kestrel用于执行异步实体框架操作或其他(网络)I / O的线程池的大小? Is it the same thread-pool it uses to accept HTTP requests? 它用于接受HTTP请求的线程池是否相同?
UPDATE: thanks to this and this article by Stephen Cleary I now understand that it will run "kind-of-single-threadly" by default, ie:, at any given time there will be exactly one thread within the SynchronizationContext of a given HTTP request, but this thread may be switched to another when some async operation is marked as finished and task is resumed. 更新:感谢本文由斯蒂芬·克利我现在明白了,它将运行“之类-的单螺接到”在默认情况下,即:,在任何时候都会有一个给定的HTTP的SynchronizationContext恰好内一个线程请求,但是当某些异步操作被标记为完成并且任务恢复时,该线程可以切换到另一个线程。 I also learned that it is possible to dispatch any async method "out of a given SynchronizationContext" to a thread-pool thread by using ConfigureAwait(false) . 我还了解到,可以通过使用ConfigureAwait(false)将任何异步方法“从给定SynchronizationContext之外”分派到线程池线程。 Still, I don't know how to control the number of thread-pool threads and if it is the same pool that is used by Kestrel to accept HTTP requests. 仍然,我不知道如何控制线程池线程的数量,以及它是否与Kestrel用来接受HTTP请求的池相同。

As I understand if all I/O is done asynchronously properly then the the size of these pools (whether it's just 1 thread-pool in fact or 2 separate) does not have anything to do with the number of outgoing connections being opened to external resources like DB, right? 据我了解,如果所有I / O均正确地异步完成,则这些池的大小(实际上实际上是1个线程池还是2个单独的池)与向外部资源打开的传出连接数无关像DB,对吗? (threads running an asynchronous code will be continuously accepting new HTTP requests and as a result of their processing will be opening outgoing connections (to DB or to some web server to fetch some resource from the web) as fast as they can without any blocking) (运行异步代码的线程将不断接受新的HTTP请求,并且由于它们的处理,它们将尽快打开出站连接(连接到DB或连接到某些Web服务器以从Web上获取一些资源),而不会受到任何阻塞)
This in turn means that if I don't want to kill my DB I should set a sane limit of its connection pool, and make sure that awaiting for an available connection is also done asynchronously, right? 这反过来意味着,如果我不想杀死我的数据库,则应该对其连接池设置一个合理的限制,并确保等待可用连接也以异步方式完成,对吗?
Assuming that I'm correct I would still want to be able to limit the number of thread-pool threads in case some long lasting I/O operation is mistakenly done synchronously to prevent large number of threads being spawned as a result of large numbers of threads being blocked on this synchronous operation (ie: I think it's better to limit performance of my app rather than make it spawn insane number of threads due to such bug) 假设我是正确的,我仍然希望能够限制线程池线程的数量,以防错误地同步执行了一些长时间的I / O操作,以防止由于大量的I / O操作而产生大量的线程。线程在此同步操作上被阻止(即:我认为最好限制我的应用程序的性能,而不是由于此类错误而使它产生疯狂的线程数)

I also have "just out of curiosity" question: why actually async/await is not implemented to execute async tasks on the same thread? 我也有“出于好奇”的问题:为什么实际上未实现异步/等待以在同一线程上执行异步任务? I don't know how to implement it on windows but on Unix this could be implemented using either select/poll system calls or signals, so I believe that there's a way to achieve this on windows as well and it would be a really cool language feature indeed. 我不知道如何在Windows上实现它,但是在Unix上可以使用选择/轮询系统调用或信号来实现,所以我相信也有一种方法可以在Windows上实现,这将是一种非常酷的语言功能确实如此。
UPDATE: if I understand correctly this is almost how things will work if my SynchronizationContext is not null: ie code will run "kind-of-single-threadly": at any given time there will be exactly one thread within a given SynchronizationContext. 更新:如果我正确理解,如果我的SynchronizationContext不为null,这几乎是所有事情的工作方式:即代码将“单线程运行”:在任何给定时间,给定SynchronizationContext中将只有一个线程。 However the way it is implemented will make a synchronous method waiting for async task to deadlock, right? 但是,它的实现方式将使同步方法等待异步任务陷入僵局,对吗? (runtime will schedule marking of the task as finished to run on the same thread that is waiting for this task to be finished) (运行时将计划将任务标记为已完成,以在等待该任务完成的同一线程上运行)

Thanks! 谢谢!

await uses the value of SynchronizationContext.Current to schedule the continuations. await使用SynchronizationContext.Current的值来计划继续。 In a console app there is no current synchronization context by default; 在控制台应用程序中,默认情况下没有当前的同步上下文。 so it's just going to use thread pool threads to schedule the continuations. 因此,仅使用线程池线程来调度连续性。 In applications with a message loop, such as winforms, WPF, or winphone applications the message loop will set the current sync context to one that will send all messages to the message loop, thereby running them on the UI thread. 在具有消息循环的应用程序(例如winforms,WPF或winphone应用程序)中,消息循环会将当前同步上下文设置为将所有消息发送到消息循环的同步上下文,从而在UI线程上运行它们。

In ASP applications there will also be a current synchronization context, but it's not a particular thread. 在ASP应用程序中,还将有一个当前的同步上下文,但这不是特定的线程。 Rather, when it's time to run the next message for the sync context it takes a thread pool thread, sets it up with the request data for the right request, and then has it run the message. 相反,当需要为同步上下文运行下一条消息时,它将获取一个线程池线程,使用正确的请求的请求数据对其进行设置,然后运行该消息。 This means that when using the sync context in an ASP application you know that there is never more than one operation running at a time, but it's not necessarily a single thread handling every response. 这意味着在ASP应用程序中使用同步上下文时,您会知道一次运行的操作不会超过一个,但不一定是单个线程来处理每个响应。

By default when there is not SynchronizationContext or when ConfigureAwait(false) is called on awaited task, its continuation will run on CLR maintained thread-pool that can be accessed using static methods from ThreadPool class 默认情况下,当没有SynchronizationContext或在等待的任务上调用ConfigureAwait(false)时,其延续将在CLR维护的线程池上运行,可以使用ThreadPool类中的静态方法进行访问
in .net core methods from ThreadPool to control its size ( setMaxThreads and setMinThreads ) are not implemented yet: 在.net核心方法中,从ThreadPool来控制其大小( setMaxThreadssetMinThreads )尚未实现:
https://github.com/dotnet/cli/issues/889 https://github.com/dotnet/cli/issues/889
https://github.com/dotnet/corefx/issues/5920 https://github.com/dotnet/corefx/issues/5920
it is possible however to set its size statically in project.json file as explained here: 但是可以在project.json文件中静态设置其大小,如下所示:
https://github.com/dotnet/cli/blob/rel/1.0.0/Documentation/specs/runtime-configuration-file.md#runtimeoptions-section-runtimeconfigjson https://github.com/dotnet/cli/blob/rel/1.0.0/Documentation/specs/runtime-configuration-file.md#runtimeoptions-section-runtimeconfigjson

kestrel has its own pool for async processing of requests with libuv: it's controlled by static ThreadCount property of KestrelServerOptions class. kestrel有自己的库,用于使用libuv异步处理请求:它由KestrelServerOptions类的静态ThreadCount属性控制。

related articles: 相关文章:
http://blog.stephencleary.com/2012/02/async-and-await.html http://blog.stephencleary.com/2012/02/async-and-await.html
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/ https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
https://msdn.microsoft.com/en-us/magazine/gg598924.aspx https://msdn.microsoft.com/zh-CN/magazine/gg598924.aspx

thx to @Servy and @Damien_The_Unbeliever for the hints that allowed me to find it 感谢@Servy和@Damien_The_Unbeliever,以获取让我找到它的提示

Worth to mention here, as the question is with regards to dot net Core , that unlike .Net there isn't a SynchronizationContext in dot net core 在这里值得一提的是,因为问题是关于点网核心的 ,与.Net不同的点网核心中 没有 SynchronizationContext

your async Task will be run using any thread for the thread pool. 您的异步任务将使用线程池的任何线程运行。

ConfigureAwait isn't relevant, unless you think it will be used in .Net ConfigureAwait无关紧要,除非您认为它将在.Net中使用

see https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html 参见https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html

and ConfigureAwait(false) relevant in ASP.NET Core? ConfigureAwait(false)在ASP.NET Core中相关?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM