首页 > 后端开发 > C++ > 正文

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

P粉602998670
发布: 2025-07-03 11:16:01
原创
662人浏览过

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 <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,则表示操作正在异步执行。

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

接下来,我们需要处理 I/O 完成的通知。这就是完成端口发挥作用的地方。

  1. 创建完成端口: 使用 CreateIoCompletionPort 函数创建一个完成端口。

    HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    if (hCompletionPort == NULL) {
        std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl;
        return 1;
    }
    登录后复制
  2. 将文件句柄关联到完成端口: 使用 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;
    }
    登录后复制
  3. 发起异步 I/O 请求: 使用 ReadFile 或 WriteFile 函数发起异步 I/O 请求,并将 OVERLAPPED 结构体传递给函数。

  4. 等待 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 结构体的指针。

  5. 处理 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中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号