在Windows
中,同步I/O
和异步I/O
是两种不同的数据输入输出处理方式,而I/O
完成端口(IOCP
)是一种特定的高效异步I/O模型。
I/O
(Synchronous I/O)同步I/O操作要求发起I/O请求的线程等待操作完成才能继续执行。这意味着线程在等待I/O完成时会被阻塞。
Windows
中打开文件或者设备都可以使用CreateFile
函数,Windows
系统为我们封装了底层设备I/O
的细节让我们可以像操作文件一样操作串口并口等设备。
优点:
缺点:
#include <windows.h>
#include <iostream>
void ReadFromFile(const wchar_t* filename) {
HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Error opening file" << std::endl;
return;
}
char buffer[128];
DWORD bytesRead;
BOOL result = ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL);
if (result) {
std::cout << "Read " << bytesRead << " bytes." << std::endl;
} else {
std::cerr << "Error reading file" << std::endl;
}
CloseHandle(hFile);
}
备注:注意,在同步模式下,示例中 CreateFile
中 参数 FILE_ATTRIBUTE_NORMAL
,也可以是NULL
I/O
设备对设配I/O进行读写最方便时ReadFile和WriteFile 函数原型如下:
BOOL ReadFile(
HANDLE hFile, // 设备句柄
LPVOID lpBuffer, //数据缓存
DWORD nNumberOfBytesToRead, // 告诉设备需要读取多少字节
LPDWORD lpNumberOfBytesRead, // 真实读取的字节
LPOVERLAPPED lpOverlapped // 同步I/O 此参数应该为NULL,异步I/O时需要传入LPOVERLAPPED
);
BOOL WriteFile(
HANDLE hFile,// 设备句柄
LPCVOID lpBuffer,//数据缓存
DWORD nNumberOfBytesToWrite,// 告诉设备需要写入多少字节
LPDWORD lpNumberOfBytesWritten,真实写入的字节
LPOVERLAPPED lpOverlapped// 同步I/O 此参数应该为NULL,异步I/O时需要传入LPOVERLAPPED
);
I/O
(Asynchronous I/O)异步I/O允许线程发起一个I/O操作后立即继续执行,不需要等待I/O操作完成。操作系统会在I/O操作完成后通知应用程序。
优点:
缺点:
#include <windows.h>
#include <iostream>
void CALLBACK FileIOCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
std::cout << "Read " << dwNumberOfBytesTransfered << " bytes." << std::endl;
}
void ReadFromFileAsync(const wchar_t* filename) {
HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Error opening file" << std::endl;
return;
}
char buffer[128];
OVERLAPPED ol = {0};
BOOL result = ReadFileEx(hFile, buffer, sizeof(buffer), &ol, FileIOCompletionRoutine);
if (!result) {
std::cerr << "Error reading file" << std::endl;
}
SleepEx(INFINITE, TRUE); // Wait for the I/O to complete
CloseHandle(hFile);
}
I/O
完成端口(IOCP
)IOCP是Windows提供的一种高效的线程池技术,用于处理大量的异步I/O操作。
优点:
缺点:
IOCP
由于IOCP的实现相对复杂,涉及到创建完成端口、关联文件或套接字句柄、处理完成通知等多个步骤,以下是一个简化的IOCP使用示例,展示了如何创建完成端口、如何将文件句柄与完成端口关联,以及如何在工作线程中处理I/O完成事件。
#include <windows.h>
#include <iostream>
// 工作线程函数
DWORD WINAPI WorkerThread(LPVOID lpParam) {
HANDLE hCompletionPort = (HANDLE)lpParam;
DWORD bytesTransferred;
ULONG_PTR completionKey;
OVERLAPPED* pOverlapped;
while (GetQueuedCompletionStatus(hCompletionPort, &bytesTransferred, &completionKey, &pOverlapped, INFINITE)) {
// 处理完成的I/O请求
// ...
}
return 0;
}
void UseIOCP(const wchar_t* filename) {
// 打开文件
HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Error opening file" << std::endl;
return;
}
// 创建完成端口
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (hCompletionPort == NULL) {
std::cerr << "Error creating IOCP" << std::endl;
CloseHandle(hFile);
return;
}
// 将文件句柄与完成端口关联
if (CreateIoCompletionPort(hFile, hCompletionPort, 0, 0) == NULL) {
std::cerr << "Error associating file handle with IOCP" << std::endl;
CloseHandle(hCompletionPort);
CloseHandle(hFile);
return;
}
// 创建工作线程
HANDLE hThread = CreateThread(NULL, 0, WorkerThread, hCompletionPort, 0, NULL);
if (hThread == NULL) {
std::cerr << "Error creating worker thread" << std::endl;
CloseHandle(hCompletionPort);
CloseHandle(hFile);
return;
}
// 发起异步读取操作
char buffer[1024];
OVERLAPPED ol = {0};
if (!ReadFile(hFile, buffer, sizeof(buffer), NULL, &ol)) {
if (GetLastError() != ERROR_IO_PENDING) {
std::cerr << "Error reading file" << std::endl;
CloseHandle(hThread);
CloseHandle(hCompletionPort);
CloseHandle(hFile);
return;
}
}
// 等待工作线程完成
WaitForSingleObject(hThread, INFINITE);
// 清理资源
CloseHandle(hThread);
CloseHandle(hCompletionPort);
CloseHandle(hFile);
}
在这个示例中,我们首先打开一个文件并创建一个完成端口。然后,我们将文件句柄与完成端口关联,并创建一个工作线程来处理I/O
完成事件。接着,我们发起一个异步读取操作。工作线程将在I/O
操作完成时被唤醒,以处理该事件。
请注意,这个示例是高度简化的。在实际应用中,你需要处理多个并发的I/O
操作,正确处理I/O
操作的完成或失败,并且可能需要管理多个工作线程。此外,你还需要设置OVERLAPPED
结构体,以便在I/O操作完成时能够正确地获取到操作的上下文信息。
参考文献:
1、Windows同步设备I/O与异步设备I/O
2、追求极致:Windows异步IO各种模式性能对比
3、浅谈windows下高性能服务器模型IOCP