繁体   English   中英

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

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

我是Java世界中的ASP.NET Core和C#的新手,对于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";
}

会写

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

这意味着,如果当前线程繁忙,这仅仅是将“异步”方法委派给单独线程的一种方法,这与上述文章所指出的几乎完全相反

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

如此以来的异步方法“延续”显然或者至少可以被安排在不同的线程上运行,我有一些问题,似乎对C#应用程式的基本可扩展性:

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

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

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

我也有“出于好奇”的问题:为什么实际上未实现异步/等待以在同一线程上执行异步任务? 我不知道如何在Windows上实现它,但是在Unix上可以使用选择/轮询系统调用或信号来实现,所以我相信也有一种方法可以在Windows上实现,这将是一种非常酷的语言功能确实如此。
更新:如果我正确理解,如果我的SynchronizationContext不为null,这几乎是所有事情的工作方式:即代码将“单线程运行”:在任何给定时间,给定SynchronizationContext中将只有一个线程。 但是,它的实现方式将使同步方法等待异步任务陷入僵局,对吗? (运行时将计划将任务标记为已完成,以在等待该任务完成的同一线程上运行)

谢谢!

await使用SynchronizationContext.Current的值来计划继续。 在控制台应用程序中,默认情况下没有当前的同步上下文。 因此,仅使用线程池线程来调度连续性。 在具有消息循环的应用程序(例如winforms,WPF或winphone应用程序)中,消息循环会将当前同步上下文设置为将所有消息发送到消息循环的同步上下文,从而在UI线程上运行它们。

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

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

kestrel有自己的库,用于使用libuv异步处理请求:它由KestrelServerOptions类的静态ThreadCount属性控制。

相关文章:
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/zh-CN/magazine/gg598924.aspx

感谢@Servy和@Damien_The_Unbeliever,以获取让我找到它的提示

在这里值得一提的是,因为问题是关于点网核心的 ,与.Net不同的点网核心中 没有 SynchronizationContext

您的异步任务将使用线程池的任何线程运行。

ConfigureAwait无关紧要,除非您认为它将在.Net中使用

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

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

暂无
暂无

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

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