asp.net核心和kestrel线程池中的异步处理

mor*_*wai 9 c# async-await asp.net-core

我是ASP.NET Core和C#的新手,一般来自Java世界,我对async/await关键字如何工作有点困惑:本文解释了如果一个方法被标记为异步,则意味着

"此方法包含涉及等待异步操作的控制流,因此将由编译器重写为连续传递样式,以确保异步操作可以在正确的位置恢复此方法." 异步方法的重点在于它当前线程尽可能多

所以我认为在异步方法和在字节码/虚拟机级别上实现的同一线程上运行的调用者之间传递控制是一种非常酷的方式.我的意思是我期望下面的代码

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";
}
Run Code Online (Sandbox Code Playgroud)

会写的

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

通过从传递控制doSthAsyncMainawait关键字,然后返回到doSthAsynctask.Wait()(在单个线程和所有这种情况发生的).

但是上面的代码实际上是打印的

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

这意味着它只是一种将"异步"方法委托给单独线程的方法,如果当前线程忙,这几乎上述文章所述的相反:

方法上的"async"修饰符并不意味着"此方法被自动调度为在工作线程上异步运行"

因此,由于异步方法的"延续"显然或者至少可以安排在不同的线程上运行,因此我有一些问题似乎是C#应用程序可伸缩性的基础:

每个异步方法是否可以在新生成的线程上运行,或者C#运行时是否为此目的维护特殊的线程池,在后一种情况下,如何控制此池的大小? 更新:感谢@Servy我现在知道,对于CMD-line应用程序,它将是一个线程池线程,但我仍然不知道如何控制这些线程池线程的数量).

在ASP.NET Core的情况下:如何控制Kestrel用于执行异步实体框架操作或其他(网络)I/O的线程池的大小?它是否与用于接受HTTP请求的线程池相同?
更新:感谢本文由斯蒂芬·克利里我现在明白了,它会在任何给定时间运行"之类-的单螺接到"在默认情况下,即:,会有一个给定的HTTP的SynchronizationContext恰好内一个线程请求,但当某些异步操作被标记为已完成并且任务恢复时,此线程可能会切换到另一个.我还了解到,可以通过使用将任何异步方法"从给定的SynchronizationContext中"分配给线程池线程ConfigureAwait(false).不过,我不知道如何控制线程池线程的数量,以及它是否与Kestrel用于接受HTTP请求的池相同.

据我所知,如果所有I/O都是异步完成的,那么这些池的大小(无论是实际上只有1个线程池还是2个独立的)都与打开到外部资源的传出连接数没有任何关系像DB,对吗?(运行异步代码的线程将继续接受新的HTTP请求,并且由于它们的处理将打开传出连接(到DB或某些Web服务器以从Web获取某些资源),尽可能快地进行,而不会有任何阻塞)
这反过来意味着如果我不想杀死我的数据库,我应该设置其连接池的合理限制,并确保等待可用连接也是异步完成的,对吧?
假设我是正确的,我仍然希望能够限制线程池线程的数量,以防一些长时间的I/O操作被错误地同步进行,以防止由于大量的线程而产生大量线程线程在这个同步操作上被阻塞(即:我认为最好限制我的应用程序的性能,而不是因为这样的bug而导致它产生疯狂的线程数)

我也有"只是出于好奇"的问题:为什么实际上没有实现async/await来在同一个线程上执行异步任务?我不知道如何在Windows上实现它,但在Unix上,这可以使用select/poll系统调用或信号来实现,所以我相信有一种方法可以在Windows上实现这一点,它将是一个非常酷的语言功能确实.
更新:如果我理解正确的话,这是几乎如果我的SynchronizationContext不为空的东西是如何工作的:即代码将运行"样单的- -螺接到":在任何时候都会有一个给定的SynchronizationContext内只有一个线程.但是它的实现方式会使同步方法等待异步任务死锁,对吧?(运行时会将任务标记为已完成,以便在等待完成此任务的同一线程上运行)

谢谢!

Ser*_*rvy 10

await使用值SynchronizationContext.Current来安排延续.在控制台应用程序中,默认情况下没有当前的同步上下文; 所以它只是使用线程池线程来安排延续.在具有消息循环的应用程序(例如winforms,WPF或winphone应用程序)中,消息循环将当前同步上下文设置为将所有消息发送到消息循环的上下文,从而在UI线程上运行它们.

在ASP应用程序中,还会有一个当前的同步上下文,但它不是一个特定的线程.相反,当需要为同步上下文运行下一条消息时,它需要一个线程池线程,使用正确请求的请求数据进行设置,然后让它运行该消息.这意味着在ASP应用程序中使用同步上下文时,您知道一次只能运行多个操作,但它不一定是处理每个响应的单个线程.


mor*_*wai 5

默认情况下,当没有SynchronizationContext或者ConfigureAwait(false)在等待任务上调用时,它的继续将在CLR维护的线程池上运行,可以使用 .net核心方法中的ThreadPool从静态方法访问来控制其大小(setMaxThreadssetMinThreads)尚未实现:https://github.com/dotnet/cli/issues/889 https://github.com/dotnet/corefx/issues/5920 然而,可以在文件中静态设置其大小,如下所述:https ://github.com/dotnet/cli/blob/rel/1.0.0/Documentation/specs/runtime-configuration-file.md#runtimeoptions-section-runtimeconfigjson
ThreadPool


project.json

红隼有自己与libuv请求的异步处理池:它是由控制的静态ThreadCount属性KestrelServerOptions类.

相关文章:
http://blog.stephencleary.com/2012/02/async-and-await.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://msdn.microsoft.com/en-us/magazine/gg598924.aspx

thx到@Servy和@Damien_The_Unbeliever提供了允许我找到它的提示