如何用C++实现异步文件IO 重叠IO和完成端口技术解析

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

如何用C++实现异步文件IO 重叠IO和完成端口技术解析

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

如何用C++实现异步文件IO 重叠IO和完成端口技术解析

解决方案:

如何用C++实现异步文件IO 重叠IO和完成端口技术解析

首先,我们需要理解重叠 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,则表示操作正在异步执行。

如何用C++实现异步文件IO 重叠IO和完成端口技术解析

接下来,我们需要处理 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 请求: 使用 ReadFileWriteFile 函数发起异步 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] = '';    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和完成端口技术解析的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1465152.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月18日 15:37:05
下一篇 2025年12月18日 15:37:14

相关推荐

发表回复

登录后才能评论
关注微信