Я пытаюсь понять пул потоков завершения iocp и 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, что случилось?
WAITFOR DELAY '00:00:10';select count(1) from aa;
, но он все еще использовал рабочий поток. Я действительно озадачен этим. - person Hourglass   schedule 17.09.2019await Task.Delay()
, здесь нет фактической операции ввода-вывода. Когда обратный вызов ввода-вывода использует поток ввода-вывода, а когда обратный вызов ввода-вывода использует рабочий поток? Вы имеете в виду, что только привязка iocp непосредственно в коде .NET будет использовать поток ввода-вывода, а SQL может привязать iocp в библиотеке C, поэтому .net использует рабочий поток вместо потока ввода-вывода? Спасибо за ответ. - person Hourglass   schedule 17.09.2019