• 【重叠I/O之系列三】I/O完成端口


    一   串行模式和并行模式

      一般一个服务应用程序采用以下两个架构模型之一:

    • 串行模式  一个线程等待一个客户发出的请求,当请求到达的时候,线程会被换醒来处理客户的请求。
    • 并发模式。一个线程等待一个客户发出的请求,当请求到达的时候,线程会创建一个新的线程来处理客户的请求,而当前线程则会进入下一次循环继续等待客户的请求。

      在当前多客户请求的环境下,串行模式远远不能满足当前服务器的性能要求。串行模式每次只能处理一个客户请求,而现在的计算机大多数都是多处理器的机器,所以不充分发挥出多处理器机器的优势。所以串行模式只能应用于一些简单的服务器应用程序,向Ping服务器程序。

      由于串行模式的受限,所以在服务器应用中一般采用并发模式。在并发模式中,当服务器接受到一个客户请求时,会创建一个新的线程来处理客户请求。这种模式的优点是显而易见的,等待请求的线程只有很少的工作需要做。当客户请求到达的时候,该线程会被唤醒,创建一个新的处理线程,然后继续睡大觉。所以,服务器可以很快的对客户请求进行相应。而同时,每个客户请求都有一个线程来对其进行处理,能够发挥多处理器机器的优势。但是,这并不是没有缺点的:当一个机器中的线程远大于处理器的数目时,频繁的线程切换会极大的浪费CPU的性能。例如,如果有2000个客户请求,当前的并发模型就需要2000个线程来处理请求,每个线程都需要大量的虚拟内存空间。系统默认的每个线程消耗1MB的堆栈空间,则2000个线程消耗2G虚拟地址空间,而且线程上下文的切换会增加页错误,同时会CPU在线程的上下文切换上需要浪费太多的时间,而线程真正得到CPU执行时间很少。同时,虽然创建一个线程的开销相对于创建进程小的多,但是它的开销仍然不小。如果应用程序能够在初始化的时候创建一个线程池,这样当需要调用线程的时候直接从线程池中获取,这样便可以大大的提升性能了。

      正是为了解决以上问题,Windows提出了I/O完成端口解决方案,使得我们在线程池中创建有限数量的服务器线程,这个线程池的调用由Windows系统来维护。同时使用大量的套接字或命名管道句柄,让每个句柄都与不通的客户相关联,句柄不和服务器线程配对。当一个重叠的I/O完成后,会被加入到完成队列中,系统会从线程池中唤醒一个线程来处理完成的I/O请求。

    二 I/O完成端口解析

      1 创建I/O完成端口

      创建I/O完成端口调用函数:

      

    HANDLE
    WINAPI
    CreateIoCompletionPort(
        __in     HANDLE FileHandle,
        __in_opt HANDLE ExistingCompletionPort,
        __in     ULONG_PTR CompletionKey,
        __in     DWORD NumberOfConcurrentThreads
        );
    • FileHandle

      FileHandle是要添加到端口中的一个重叠的设备句柄,注意一个I/O完成端口是以OVERLAPPED模式打开的文件句柄的集合。如果创建一个新的I/O完成端口,则此值为INVALID_HANDLE_VALUE,同时参数ExistingCompletionPort为空。

    • ExistingCompletionPort

       ExistingCompletionPort表示文件句柄将要关联的端口。

    • CompletionKey

      CompletionKey指定在完成包中为FileHandle所包含的键值。这个键值可以指向一个结构的指针,该结构包含包含了诸如操作类型、句柄以及指向数据缓冲区指针等信息。

    • NumberOfConcurrentPort

      表示允许并发执行的线程的最大数量,超过这个数量的等待端口的线程将保持阻塞状态。如果为0,那么最大的并发执行的线程数就是系统中处理器的数目。当ExistingCompletionPort不为NULL时,这个值会被忽略。

      这个函数其实是完成两个任务:一是创建一个I/O完成端口,二是将一个设备与一个I/O完成端口关联起来。只是该函数将所有的参数放到一起,显得有点复杂。我们可以将其进行功能拆分,创建两个小函数来分别完成以上两个任务:

      

    //创建新的I/O完成端口
    HANDLE CreateNewCompletionPort(DWORD dwNumOfConcurrentThread)
    {
    
        return CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, dwNumOfConcurrentThread);
    }
    
    //将设备和IO完成端口相关联
    BOOL AssociateDeviceWithCompletionPort(HANDLE hDevice, HANDLE hCompletionPort, DWORD dwCompKey)
    {
        HANDLE h = CreateIoCompletionPort(hDevice,hCompletionPort,dwCompKey,0);
        return (h == NULL);
    }
  • 相关阅读:
    java IO流之详细总结
    位运算了解与复习多线程
    java 常见面试题总结(一)
    复习集合框架
    【面试题】java中高以上必会技能
    python-项目流程分析及优化查询方法
    python-day97--django-ModelForm
    python-day97--git协同开发
    python-day96--git版本控制
    python-day91--同源策略与Jsonp
  • 原文地址:https://www.cnblogs.com/1314NNNKKK/p/3712559.html
Copyright © 2020-2023  润新知