Windows 异步IO操作(三)
IO完成端口
我们前边所学习的三种异步IO方式, 都是串行模型来进行异步IO操作
IO完成端口是并行模型, 是多线程的
多线程概述
在单核时代, 多线程是模拟出来的并行, 是要进行线程切换的.
在多核时代, 就是真正的多线程了, 每个核都可以运行一个线程(无切换), 也可以进行线程切换.
多进程
- 一个进程只有一个线程, 那么它只能干一件事情
- 如果需要干两件事情, 那么就开2个进程
- 由于进程间交互和切换会浪费大量资源, 所以有了
多线程
多线程
- 一个进程有多个线程
- 它们之间的交互和切换不会有很大的资源浪费
它天生就是多线程并行模型
IOCP初窥
IO完成端口是Windows提供给我们的一整套的东西.
它会经过如下几个步骤
一个完成端口中, 会首先创建队列
- 它包含了我们的设备
- 可以有多个设备对应我们一个完成端口
创建设备操作队列
- 对我们的设备进行挨个的操作
创建线程池
- 多个线程来完成设备操作
首先, 我们需要通过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);
// 所以, 可以进行简写未完待续...
如有错误,请提出指正!谢谢.
本文由 花心胡萝卜 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: 2017-06-18 at 06:36 am