c++++ 中异步文件 i/o 的实现核心在于使用重叠 i/o 和完成端口技术,以避免主线程阻塞。1. 使用 overlapped 结构体发起异步 i/o 请求,2. 创建并关联完成端口以处理完成通知,3. 通过 getqueuedcompletionstatus 等待并处理 i/o 完成结果。此外,需注意错误处理和资源管理,如检查 getlasterror 和关闭句柄。

异步文件 I/O 在 C++ 中实现的核心在于让文件操作不阻塞主线程,从而提高程序的响应性和并发性。这通常涉及使用操作系统提供的重叠 I/O (Overlapped I/O) 和完成端口 (Completion Ports) 技术。简单来说,就是发起 I/O 请求后立即返回,让操作系统在后台处理,完成后通知程序。

解决方案:

首先,我们需要理解重叠 I/O 的概念。重叠 I/O 允许我们发起一个 I/O 操作,而无需等待其完成。我们需要一个 OVERLAPPED 结构体来传递 I/O 请求的相关信息。
立即学习“C++免费学习笔记(深入)”;
#include #include #include bool AsyncReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPOVERLAPPED pOverlapped) { return ReadFile(hFile, buffer, bytesToRead, NULL, pOverlapped);}
这段代码只是一个简单的开始,它展示了如何使用 ReadFile 函数发起一个异步读取操作。关键在于最后一个参数 pOverlapped,它告诉操作系统这是一个异步操作。如果 ReadFile 返回 FALSE,并不一定意味着失败,而是可能操作正在进行中,需要检查 GetLastError() 的返回值。如果是 ERROR_IO_PENDING,则表示操作正在异步执行。

接下来,我们需要处理 I/O 完成的通知。这就是完成端口发挥作用的地方。
创建完成端口: 使用 CreateIoCompletionPort 函数创建一个完成端口。
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);if (hCompletionPort == NULL) { std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl; return 1;}
将文件句柄关联到完成端口: 使用 CreateIoCompletionPort 函数将文件句柄与完成端口关联起来。
HANDLE hFile = CreateFile(L"test.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);if (hFile == INVALID_HANDLE_VALUE) { std::cerr << "CreateFile failed: " << GetLastError() << std::endl; return 1;}HANDLE hAssociatedPort = CreateIoCompletionPort(hFile, hCompletionPort, (ULONG_PTR)hFile, 0);if (hAssociatedPort == NULL) { std::cerr << "Associate file handle with completion port failed: " << GetLastError() << std::endl; return 1;}
发起异步 I/O 请求: 使用 ReadFile 或 WriteFile 函数发起异步 I/O 请求,并将 OVERLAPPED 结构体传递给函数。
等待 I/O 完成: 使用 GetQueuedCompletionStatus 函数等待 I/O 完成的通知。
DWORD bytesTransferred;ULONG_PTR completionKey;LPOVERLAPPED pOverlapped;BOOL bRet = GetQueuedCompletionStatus(hCompletionPort, &bytesTransferred, &completionKey, &pOverlapped, INFINITE);if (bRet == FALSE) { std::cerr << "GetQueuedCompletionStatus failed: " << GetLastError() << std::endl; return 1;}std::cout << "Bytes transferred: " << bytesTransferred << std::endl;
GetQueuedCompletionStatus 会一直阻塞,直到完成端口收到一个 I/O 完成的通知。当 I/O 完成时,bytesTransferred 变量会包含实际传输的字节数,completionKey 变量会包含与文件句柄关联的完成键,pOverlapped 变量会包含指向 OVERLAPPED 结构体的指针。
处理 I/O 完成: 在 GetQueuedCompletionStatus 返回后,我们可以处理 I/O 操作的结果。例如,可以检查 bytesTransferred 变量的值,以确定实际传输的字节数。
完整的示例代码:
#include #include #include int main() { HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (hCompletionPort == NULL) { std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl; return 1; } HANDLE hFile = CreateFile(L"test.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (hFile == INVALID_HANDLE_VALUE) { std::cerr << "CreateFile failed: " << GetLastError() << std::endl; return 1; } HANDLE hAssociatedPort = CreateIoCompletionPort(hFile, hCompletionPort, (ULONG_PTR)hFile, 0); if (hAssociatedPort == NULL) { std::cerr << "Associate file handle with completion port failed: " << GetLastError() << std::endl; return 1; } char buffer[1024]; OVERLAPPED overlapped = {0}; overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //手动重置事件 if (!ReadFile(hFile, buffer, sizeof(buffer) - 1, NULL, &overlapped)) { if (GetLastError() != ERROR_IO_PENDING) { std::cerr << "ReadFile failed: " << GetLastError() << std::endl; CloseHandle(hFile); CloseHandle(hCompletionPort); return 1; } } DWORD bytesTransferred; ULONG_PTR completionKey; LPOVERLAPPED pOverlapped; BOOL bRet = GetQueuedCompletionStatus(hCompletionPort, &bytesTransferred, &completionKey, &pOverlapped, INFINITE); if (bRet == FALSE) { std::cerr << "GetQueuedCompletionStatus failed: " << GetLastError() << std::endl; CloseHandle(hFile); CloseHandle(hCompletionPort); return 1; } buffer[bytesTransferred] = '