C# IOCP线程-澄清?

C# IOCP线程-澄清?,c#,async-await,iocp,C#,Async Await,Iocp,在阅读以下内容后: 设备完成其工作后(IO操作)-它通知CPU 通过中断 但是,该“完成”状态仅适用于 存在于操作系统级别;进程有自己的内存空间,必须 被通知 因为库/BCL使用的是标准的p/Invoke 重叠的I/O系统,它已将句柄注册到 I/O完成端口(IOCP),它是线程池的一部分 因此,一个I/O线程池线程被短暂借用来执行APC, 通知任务已完成 我对粗体部分很感兴趣: 如果我理解正确,IO操作完成后,它必须通知执行IO操作的实际进程 问题1: 这是否意味着它会为每个完成的IO操作

在阅读以下内容后:

设备完成其工作后(IO操作)-它通知CPU 通过中断

但是,该“完成”状态仅适用于 存在于操作系统级别;进程有自己的内存空间,必须 被通知

因为库/BCL使用的是标准的p/Invoke 重叠的I/O系统,它已将句柄注册到 I/O完成端口(IOCP),它是线程池的一部分

因此,一个I/O线程池线程被短暂借用来执行APC, 通知任务已完成

我对粗体部分很感兴趣:

如果我理解正确,IO操作完成后,它必须通知执行IO操作的实际进程

问题1:

这是否意味着它会为每个完成的IO操作获取一个新的线程池线程?或者,这是一个专门的线程数

问题2:

看看:

for (int i=0;i<1000;i++)
    {
      PingAsync_NOT_AWAITED(i); //notice not awaited !
    }

for(int i=0;i这有点宽泛,所以让我来谈谈要点:

可以说,IOCP线程位于一个单独的线程池中—这是I/O线程设置。因此它们不会与用户线程池线程冲突(就像您在正常的
wait
操作或
ThreadPool.QueueWorkerItem
中使用的线程)

就像普通的线程池一样,它只会随着时间的推移缓慢地分配新线程。因此,即使异步响应的峰值同时发生,您也不会有1000个I/O线程

在一个完全异步的应用程序中,你不会像工作线程那样拥有超过内核数量的内核,不管是给予还是索取。这是因为你要么在做大量的CPU工作,你应该把它发布到一个普通的工作线程上,要么在做I/O工作,你应该作为一个异步操作来做

这个想法是你在I/O回调中花费很少的时间-你不会阻塞,也不会做很多CPU工作。如果你违反了这一点(比如,在回调中添加
Thread.Sleep(10000)
),那么,.NET会随着时间的推移创建成吨的IO线程-但这只是不正确的使用

现在,I/O线程与普通CPU线程有何不同?它们几乎相同,只是等待不同的信号-两者(简化警报)都只是一个
循环,而
循环通过一个方法,该方法在应用程序(或操作系统)的其他部分对新工作项排队时提供控制。主要区别在于I/O线程使用IOCP队列(OS管理),而普通工作线程有自己的队列,完全由.NET管理并可由应用程序程序员访问

请注意,不要忘记您的请求可能已同步完成。可能您正在while循环中读取TCP流,每次读取512字节。如果套接字缓冲区中有足够的数据,则多个
ReadAsync
s可以立即返回,而无需进行任何线程切换。这通常不是问题,因为I/O通常是典型应用程序中时间最密集的工作,因此不必等待I/O通常是可以的。但是,依赖于异步发生的某些部分的错误代码(即使这不能保证)很容易破坏应用程序

这是否意味着它会为每个线程获取一个新的线程池线程 已完成的IO操作?还是专用的线程数 这个

为每一个I/O请求创建一个新线程会非常低效,甚至达不到目的。相反,运行时从少量线程开始(确切数量取决于您的环境),并根据需要添加和删除工作线程(具体的算法也因您的环境而异。).NET的任何主要版本在此实现中都发生了变化,但基本思想保持不变:运行时尽最大努力创建和维护有效服务所有I/O所需的线程。在我的系统上(Windows 8.1、.NET 4.5.2)一个全新的控制台应用程序在进入
Main
的过程中只有3个线程,在请求实际工作之前,这个数字不会增加

这是否意味着我将同时拥有1000个IOCP线程池线程 (有点)在这里跑步,什么时候全部完成

否。当您发出I/O请求时,线程将在完成端口上等待获取结果,并调用注册的任何回调来处理结果(通过
BeginXXX
方法或作为任务的继续)。如果您使用任务而不等待它,则该任务将在此结束,线程将返回到线程池

如果您确实在等待它呢?1000个I/O请求的结果不会真的同时到达,因为中断不会同时到达,但假设间隔比我们需要处理它们的时间短得多。在这种情况下,线程池将继续旋转线程来处理结果,直到达到最大值,即nd任何进一步的请求都将在完成端口上排队。根据您的配置,这些线程可能需要一些时间来启动

考虑以下(故意制造的)玩具程序:

static void Main(string[] args) {
    printThreadCounts();
    var buffer = new byte[1024];
    const int requestCount = 30;
    int pendingRequestCount = requestCount;
    for (int i = 0; i != requestCount; ++i) {
        var stream = new FileStream(
            @"C:\Windows\win.ini",
            FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 
            buffer.Length, FileOptions.Asynchronous
        );
        stream.BeginRead(
            buffer, 0, buffer.Length,
            delegate {
                Interlocked.Decrement(ref pendingRequestCount);
                Thread.Sleep(Timeout.Infinite);
            }, null
        );
    }
    do {
        printThreadCounts();
        Thread.Sleep(1000);
    } while (Thread.VolatileRead(ref pendingRequestCount) != 0);
    Console.WriteLine(new String('=', 40));
    printThreadCounts();
}

private static void printThreadCounts() {
    int completionPortThreads, maxCompletionPortThreads;
    int workerThreads, maxWorkerThreads;
    ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
    ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
    Console.WriteLine(
        "Worker threads: {0}, Completion port threads: {1}, Total threads: {2}", 
        maxWorkerThreads - workerThreads, 
        maxCompletionPortThreads - completionPortThreads, 
        Process.GetCurrentProcess().Threads.Count
    );
}
在我的系统(有8个逻辑处理器)上,输出如下(结果可能因您的系统而异):

工作线程:0,完成端口线程:0,总线程数:3
工作线程:0,完成端口线程:8,总线程数:12
工作线程:0,完成端口线程:9,总线程数:13
工作线程:0,完成端口线程:11,总线程数:15
工作线程:0,完成端口线程:13,总线程数:17
工作线程:0,完成端口线程:15,总线程数:19
工人t
stream.BeginRead(
    buffer, 0, buffer.Length,
    ar => {
        stream.EndRead(ar);
        Interlocked.Decrement(ref pendingRequestCount);
    }, null
);
private static void Main(string[] args)
{
    Task.Run(() =>
    {
        int count = 0;
        while (count < 30)
        {
            int _;
            int iocpThreads;
            ThreadPool.GetAvailableThreads(out _, out iocpThreads);
            Console.WriteLine("Current number of IOCP threads availiable: {0}", iocpThreads);
            count++;
            Thread.Sleep(10);
        }
    });

    for (int i = 0; i < 30; i++)
    {
        GetUrl(@"http://www.ynet.co.il");
    }

    Console.ReadKey();
}

private static async Task<string> GetUrl(string url)
{
    var httpClient = new HttpClient();
    var response = await httpClient.GetAsync(url);
    return await response.Content.ReadAsStringAsync();
}