Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/273.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# _ThreadPoolWaitCallback.PerformWaitCallback和_IOCompletionCallback.PerformIOCompletionCallback之间的差异_C#_Threadpool_Iocp_Io Completion Ports - Fatal编程技术网

C# _ThreadPoolWaitCallback.PerformWaitCallback和_IOCompletionCallback.PerformIOCompletionCallback之间的差异

C# _ThreadPoolWaitCallback.PerformWaitCallback和_IOCompletionCallback.PerformIOCompletionCallback之间的差异,c#,threadpool,iocp,io-completion-ports,C#,Threadpool,Iocp,Io Completion Ports,我试图理解iocp和io完成线程池。据我所知,IO通过iocp和IO完成线程像http请求一样运行,以执行回调代码。但我发现文件读取、Http请求和SQL查询之间存在一些差异 class Program { static void Main(string[] args) { FileAsync().Wait(); PrintSeparate(); SqlAsync().Wait(); PrintSeparate();

我试图理解iocp和io完成线程池。据我所知,IO通过iocp和IO完成线程像http请求一样运行,以执行回调代码。但我发现文件读取、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队列


我做了多次测试,
新文件流(…)
总是使用ioThread,发生了什么事?

异步I/O的一个基本特性是它不一定要异步完成。当驱动程序启动时恰好有可用的数据时,它将不起作用。很可能对于文件,文件系统缓存会主动预缓存文件数据。http不太可能发生这种情况,因为互联网速度非常慢,因此很容易提取数据,速度比驱动程序缓冲区的速度还要快。您无法真正猜测sql将如何运行,但很可能在查询开始后立即是异步的。你永远也猜不到。@HansPassant我将sql改为
waitfordelay'00:00:10';从aa中选择计数(1),但仍使用workthread。我真的很困惑。你不能假设SQL查询的I/O请求是由.NET代码直接执行的。总有一个提供者参与其中,由dbase制造商提供,通常用C编写。它可能使用IOCP,但您看不到,因为它隐藏在非托管代码中。框架使用APM,因此完成是在工作线程而不是I/O线程上完成的。@HansPassant但是为什么要使用工作线程而不是IO线程。你说文件可能有预缓存,好吧,我理解,就像
等待Task.Delay()
,没有实际的IO操作。I/O回调何时使用I/O线程,I/O回调何时使用工作线程?您的意思是说只有在.NET代码中直接绑定iocp才会使用IO线程,而SQL可能会在C库中绑定iocp,所以.NET使用工作线程而不是IO线程?谢谢你的回复。