簡體   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