我试图了解 iocp 和 io 完成线程池。据我所知,IO 像 http 请求一样通过 iocp 和 io 完成线程来执行回调代码。但我发现文件读取、Http 请求和 SQL 查询之间存在一些差异。
class Program
{
static void Main(string[] args)
{
FileAsync().Wait();
PrintSeparate();
SqlAsync().Wait();
PrintSeparate();
HttpAsync().Wait();
Console.ReadKey();
}
private static async Task FileAsync()
{
PrintThreads($"{nameof(FileAsync)}-Entry ");
var buffer = new byte[1024];
using (var file = new FileStream(@"D:\a.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous))
{
PrintThreads($"{nameof(FileAsync)}-AfterNewFileStream");
await file.ReadAsync(buffer, 0, 1024);
PrintThreads($"{nameof(FileAsync)}-AfterReadAsync ");
PrintFirstStack();
}
}
private static async Task HttpAsync()
{
PrintThreads($"{nameof(HttpAsync)}-Entry ");
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(@"https://stackoverflow.com");
PrintThreads($"{nameof(HttpAsync)}-AfterGetAsync ");
PrintFirstStack();
await response.Content.ReadAsStringAsync();
PrintThreads($"{nameof(HttpAsync)}-AfterReadAsync");
PrintFirstStack();
}
private static async Task SqlAsync()
{
PrintThreads($"{nameof(SqlAsync)}-Entry ");
using (var connection = new SqlConnection("Data Source=***;Initial Catalog=t;User ID=sa; Password=***"))
using (var command = new SqlCommand("select count(1) from aa", connection))
{
PrintThreads($"{nameof(SqlAsync)}-AfterNewConCmd ");
await connection.OpenAsync();
PrintThreads($"{nameof(SqlAsync)}-AfterOpenAsync ");
PrintFirstStack();
var o = await command.ExecuteScalarAsync();
PrintThreads($"{nameof(SqlAsync)}-AfterExecuteAsync");
PrintFirstStack();
}
}
private static void PrintThreads(object flag)
{
ThreadPool.GetAvailableThreads(out var workThreads, out var ioThreads);
PrintThreads(flag, workThreads, ioThreads);
}
private static void PrintThreads(object flag, int workThreads, int ioThreads)
{
Console.WriteLine($"[{flag}] {nameof(workThreads)}: {workThreads}, {nameof(ioThreads)}: {ioThreads}");
}
private static void PrintSeparate()
{
Console.WriteLine("\n-------------------------------------------------------------------------------\n");
}
private static void PrintFirstStack()
{
Console.WriteLine(Environment.StackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Last());
}
}
输出:
[FileAsync-Entry ] workThreads: 2047, ioThreads: 1000
[FileAsync-AfterNewFileStream] workThreads: 2047, ioThreads: 999
[FileAsync-AfterReadAsync ] workThreads: 2046, ioThreads: 1000
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
-------------------------------------------------------------------------------
[SqlAsync-Entry ] workThreads: 2047, ioThreads: 1000
[SqlAsync-AfterNewConCmd ] workThreads: 2047, ioThreads: 1000
[SqlAsync-AfterOpenAsync ] workThreads: 2046, ioThreads: 1000
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
[SqlAsync-AfterExecuteAsync] workThreads: 2045, ioThreads: 1000
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
-------------------------------------------------------------------------------
[HttpAsync-Entry ] workThreads: 2047, ioThreads: 1000
[HttpAsync-AfterGetAsync ] workThreads: 2047, ioThreads: 999
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
[HttpAsync-AfterReadAsync] workThreads: 2047, ioThreads: 999
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
输出显示,只有 http 请求使用 ioThread 执行回调,为什么?文件和sql使用ioThread insdie框架代码,然后将用户代码回调入队到workthreadpool?
而且我测试了很多次,new FileStream(..)
总是拿一个ioThread,这是怎么回事?