【问题标题】:Async operations with I/O Completion Ports return 0 bytes transferred带有 I/O 完成端口的异步操作返回 0 字节传输
【发布时间】:2025-12-08 21:35:01
【问题描述】:

I/O 完成端口的异步操作返回 0 字节传输,尽管 I/O 操作按预期工作(我的读取缓冲区已满)。

BYTE buffer[1024] = {0};
OVERLAPPED o = {0};
HANDLE file = CreateFile(
    _T("hello.txt"),
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    NULL
);
HANDLE completion_port = CreateIoCompletionPort(
    file,
    NULL,
    0,
    0
);
ReadFile(
    file,
    buffer,
    1024,
    NULL,
    &o
);

在工作线程中:

DWORD numBytes = 0;
LPOVERLAPPED po;
GetQueuedCompletionStatus(
    completion_port,
    &numBytes,
    0,
    &po,
    INFINITE
);
GetOverlappedResult(file, &o, &numBytes, FALSE);

两个函数都返回 numBytes 中的 0 个字节,但 buffer 正在填充。这是预期的行为吗?

谢谢。

【问题讨论】:

    标签: c++ windows winapi asynchronous iocp


    【解决方案1】:

    要使GetIoCompletionPort 正常工作,您需要指定一个指向ULONG_PTR 的非空指针,以便将'key' 值写入:

    ULONG_PTR key;
    
    GetQueuedCompletionStatus(
        completion_port,
        &numBytes,
        &key,
        &po,
        INFINITE
    );
    

    要成功使用GetOverlappedResult,我相信你需要在OVERLAPPED结构中指定一个事件句柄(无论如何都强烈推荐):

    o.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
    

    像你一样连续调用这两个并没有真正实现太多——它们都告诉你同样的事情。但是,如果您确实连续调用了这两个参数,则需要通过将第三个参数更改为 CreateEvent 来将事件更改为手动重置为 TRUE。我的猜测是,您只是在尝试两者,看看是否可以让其中一个工作。考虑到所有因素,我可能只是使用GetQueuedCompletionStatus,然后就这样吧。当然,您通常会做的不仅仅是调用一次然后退出。您通常在循环中调用它,处理您已读取的当前缓冲区,然后再次调用 ReadFile 以读取另一个信息缓冲区,如下所示:

    DWORD numBytes;
    LPOVERLAPPED po;
    while (GetQueuedCompletionStatus(completion_port, &numBytes, &key, &po, INFINITE)) {
        std::cout << "\rRead: " << numBytes; // just to show it's set correctly.
        process(buffer);
        po->offset += sizeof(buffer);
        ReadFile(file, buffer, sizeof(buffer), NULL, po);
    }
    

    至少在我的机器上的快速测试中,这显示了正确读取的字节数(sizeof(buffer) 直到最后一个数据包,然后是文件的剩余大小)。

    【讨论】:

    • 是的,您对指向键的指针是正确的。你的假设也是正确的,我尝试了这两个函数来看看它们中的任何一个是否有效:-)。谢谢。
    最近更新 更多