I'm try to understand iocp and io completion threadpool. For all I know, IO operate like http request via iocp and io completion thread to execute callback code. But I found some difference between File read, Http request and SQL query.
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());
}
}
Output:
[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)
The output show me, only http request using ioThread to execute callback, why? Is file and sql using ioThread insdie framework code, and then enqueue user code callback to workthreadpool?
And I had test multi-times, new FileStream(..)
always taken a ioThread, what's happened?