PoEdu培训 Windows班 第十二课 Windows异步IO操作(三)
文章类别: 培训笔记 0 评论

PoEdu培训 Windows班 第十二课 Windows异步IO操作(三)

文章类别: 培训笔记 0 评论

Windows 异步IO操作(三)

IO完成端口

我们前边所学习的三种异步IO方式, 都是串行模型来进行异步IO操作
IO完成端口是并行模型, 是多线程的

多线程概述

单核时代, 多线程是模拟出来的并行, 是要进行线程切换的.
多核时代, 就是真正的多线程了, 每个核都可以运行一个线程(无切换), 也可以进行线程切换.

它天生就是多线程并行模型

IOCP初窥

IO完成端口是Windows提供给我们的一整套的东西.
它会经过如下几个步骤

  1. 一个完成端口中, 会首先创建队列

    • 它包含了我们的设备
    • 可以有多个设备对应我们一个完成端口
  2. 创建设备操作队列

    • 对我们的设备进行挨个的操作
  3. 创建线程池

    • 多个线程来完成设备操作

首先, 我们需要通过CreateIoCompletionPortAPI来创建一个完成端口.
它有两个功能:

CreateIoCompletionPort

// 原型
HANDLE WINAPI CreateIoCompletionPort(
  _In_     HANDLE    FileHandle,    // 设备内核对象
  _In_opt_ HANDLE    ExistingCompletionPort,    // 已存在的完成端口
  _In_     ULONG_PTR CompletionKey,     // 完成端口的一个Key值
  _In_     DWORD     NumberOfConcurrentThreads  // 创建多少个线程
);

这个函数比较难以理解, 例如如下代码:

// 创建一个完成端口
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
    NULL,
    0,
    0); // 默认值, 表示一个核心对应一个线程, 也就是说, 8核心就是8线程, 4核心就是4线程

它会成功执行完成.
它的作用是创建一个完成端口

PostQueuedCompletionStatus

插入一个请求到IO完成端口

BOOL WINAPI PostQueuedCompletionStatus(
  _In_     HANDLE       CompletionPort,     // 完成端口
  _In_     DWORD        dwNumberOfBytesTransferred, // 传输的数据长度, 用于GetQueuedCompletionStatus获取的时候
  _In_     ULONG_PTR    dwCompletionKey,    // IO完成端口的KEY
  _In_opt_ LPOVERLAPPED lpOverlapped    // OVERLAPPED结构体指针
);

GetQueuedCompletionStatus

尝试从指定的IO完成端口取数据.
如果没有完成数据包队列, 该功能将等待与完成端口关联的待处理的IO操作完成.

BOOL WINAPI GetQueuedCompletionStatus(
  _In_  HANDLE       CompletionPort,    // IO完成端口
  _Out_ LPDWORD      lpNumberOfBytes,   // 取出的数据数的指针
  _Out_ PULONG_PTR   lpCompletionKey,   // IO完成端口的KEY
  _Out_ LPOVERLAPPED *lpOverlapped,     // 指向OVERLAPPED结构体指针的指针
  _In_  DWORD        dwMilliseconds     // 等待的毫秒数
);

注意:
如果在指定时间内没有出现完成数据包, 则函数超时, 返回FALSE, 并将 *lpOverlapped 设置为NULL
如果dwMilliseconds是INFINITE, 则该函数永远不会超时
如果dwMilliseconds为零, 并且没有IO操作出现, 则该函数将立即超时.

例子

#include <Windows.h>
#include <ioapiset.h>
#include <handleapi.h>

#define IOCP_KEY_READ 1

int main()
{
    // 创建一个完成端口
    HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
        NULL,
        0,
        0);
    HANDLE hFile = CreateFile(TEXT("Demo.txt"),
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        OPEN_ALWAYS,
        FILE_FLAG_OVERLAPPED,
        NULL);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        // 绑定设备和端口
        CreateIoCompletionPort(hFile,
            hIOCP,
            IOCP_KEY_READ,
            0);

        OVERLAPPED oRead = {0};
        oRead.Offset = 0;
        DWORD dwNumberOfBytesTransferred = 100;

        // 插入一个请求
        PostQueuedCompletionStatus(hIOCP,
            dwNumberOfBytesTransferred,
            IOCP_KEY_READ,
            &oRead);

        GetQueuedCompletionStatus(hIOCP,
            &dwNumberOfBytesTransferred,
            IOCP_KEY_READ,
            &(&oRead),
            INFINITE);
    }
    else
    {
        // GetLastError()
    }

    return 0;
}

小提升

// 如下代码
    HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
        NULL,
        0,
        0);

    CreateIoCompletionPort(hFile,
        hIOCP,
        IOCP_KEY_READ,
        0);

// 等价于
    HANDLE hIOCP = CreateIoCompletionPort(hFile,
        NULL,
        IOCP_KEY_READ,
        0);

// 所以, 可以进行简写

未完待续...

如有错误,请提出指正!谢谢.

回复