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 <iostream> #include <fstream> #include <windows.h> 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 <iostream> #include <fstream> #include <windows.h> 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] = '\0'; std::cout << "Read: " << buffer << std::endl; CloseHandle(hFile); CloseHandle(hCompletionPort); CloseHandle(overlapped.hEvent); return 0; }
这个例子展示了最基本的异步文件读取。需要注意的是,错误处理和资源管理至关重要。
C++ 异步文件 I/O 的性能瓶颈有哪些?
异步 I/O 并非银弹,它也有自身的局限性。频繁的小型 I/O 操作可能会因为上下文切换的开销而降低性能。磁盘本身的 I/O 速度也是一个瓶颈。此外,不正确的缓冲管理和同步机制也可能导致性能下降。因此,需要根据具体的应用场景进行优化。例如,可以使用更大的缓冲区,或者使用多个线程来处理 I/O 请求。
如何处理 C++ 异步文件 I/O 中的错误?
错误处理是异步 I/O 中非常重要的一部分。由于 I/O 操作是在后台执行的,因此我们需要一种机制来检测和处理错误。一种常见的方法是在 OVERLAPPED 结构体中使用事件对象。当 I/O 操作完成时,操作系统会设置事件对象的状态。我们可以使用 WaitForSingleObject 函数等待事件对象的状态变为 signaled。如果 I/O 操作失败,我们可以使用 GetLastError 函数获取错误代码。
另外,GetQueuedCompletionStatus 返回值如果是 FALSE,也需要检查 GetLastError。
异步 I/O 和多线程有什么区别?
异步 I/O 和多线程都可以用来提高程序的并发性,但它们的工作方式不同。多线程通过创建多个线程来并发执行任务。每个线程都有自己的堆栈和寄存器,因此线程之间的切换需要一定的开销。异步 I/O 则利用操作系统提供的异步 I/O 功能,将 I/O 操作交给操作系统来处理。当 I/O 操作完成时,操作系统会通知程序。异步 I/O 不需要创建额外的线程,因此开销较小。然而,异步 I/O 的编程模型比多线程更复杂。选择哪种方法取决于具体的应用场景。对于 CPU 密集型的任务,多线程可能更适合。对于 I/O 密集型的任务,异步 I/O 可能更适合。
以上就是如何用C++实现异步文件IO 重叠IO和完成端口技术解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号