You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步文件IO未返回ERROR_IO_PENDING及读写功能异常求助

异步IO操作故障排查与修复

问题描述

实现异步IO操作时出现以下问题:

  • WriteFile无法输出prompt1内容到文件
  • ReadFile无法正确获取数据到chBuffer,导致switch语句进入默认分支输出option not valid!
  • WriteFile和ReadFile未返回ERROR_IO_PENDING

问题代码

#include <Windows.h>
#include <iostream>
#include <string>

class Timer {
public:
    Timer() = default;
    ~Timer() {};

    BOOL ioOperations() {
        if (!WriteFile(hFile, prompt1, bytesPrompt1, NULL, &ov1)) {
            return GetLastError(); // Minor check to see if WriteFile returns ERROR_IO_PENDING.
        }
        
        if (!ReadFile(hFile, chBuffer, dwRead, NULL, &ov2)) {
            return chBuffer[200];
            // return GetLastError(); // Minor check to see if ReadFile returns ERROR_IO_PENDING.
        }   
    }

    BOOL waitOperation() {
        BOOL waitFunc = WaitForSingleObject(hFile, dwTimeOut);
        if (waitFunc == WAIT_FAILED) {
            std::cout << "****WaitForSingleObject: An error was encountered****" << std::endl;
            return GetLastError();
        }
        else {
            return waitFunc == WAIT_OBJECT_0;
        }
    }

    BOOL funcCloseHandle() {
        BOOL closeFile = CloseHandle(hFile);
        if (closeFile == 0) {
            std::cout << "****CloseHandle: An error was encountered****" << std::endl;
            return GetLastError();
        }
        else {
            return closeFile;
        }
    }

public:
    CHAR chBuffer[201]{};

protected:
    HANDLE hFile = CreateFileA(
        "MainFile.txt",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL
    );

private:
    LPCSTR prompt1{ "Option: " };

    DWORD dwRead{ 200 };
    DWORD bytesPrompt1{ sizeof(prompt1) };
    DWORD dwTimeOut{ 5'000 };

    OVERLAPPED ov1{ 0, 0, 0, 0, CreateEvent(NULL, FALSE, FALSE, TEXT("OverlappedOne")) };
    OVERLAPPED ov2{ 0, 0, 0, 0, CreateEvent(NULL, FALSE, FALSE, TEXT("OverlappedTwo")) };
};

int main() {
    Timer tm{};

    // std::cout << "sizeof class Timer: " << sizeof(Timer) << std::endl;

    do {
        tm.ioOperations();

        switch (tm.chBuffer[0]) {
        case '1': std::cout << "option 1" << std::endl;
            break;
        case '2': std::cout << "option 2" << std::endl;
            break;
        case '3': std::cout << "exit" << std::endl;
            break;
        default: std::cout << "option not valid!" << std::endl;
            break;
        }
    } while (tm.waitOperation());

    tm.funcCloseHandle();

    return 0;
}

问题分析

  1. 字符串长度计算错误:bytesPrompt1 = sizeof(prompt1)获取的是指针大小(32位系统4字节,64位系统8字节),而非prompt1字符串的实际长度,导致WriteFile写入字节数不正确。
  2. 异步IO等待逻辑错误:WaitForSingleObject(hFile, ...)不能直接等待文件句柄完成异步操作,必须等待OVERLAPPED结构中关联的事件句柄。
  3. 未处理异步操作结果:异步操作完成后,需调用GetOverlappedResult获取实际读写字节数,确保数据同步。
  4. 函数返回值类型不匹配:ioOperations和waitOperation声明返回BOOL,但实际返回DWORD类型的错误码或无效数值,逻辑混乱。
  5. 读写顺序与同步缺失:连续调用WriteFile和ReadFile后未等待操作完成就访问chBuffer,此时异步操作尚未执行,数据仍为初始值。
  6. 文件指针位置未指定:异步文件操作时,OVERLAPPED的Offset和OffsetHigh未设置,导致读写位置不确定。

修复后的代码

#include <Windows.h>
#include <iostream>
#include <string>
#include <cstring>

class AsyncFileIO {
public:
    AsyncFileIO() : hFile(INVALID_HANDLE_VALUE) {
        // 初始化事件句柄
        ov1.hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("OverlappedWrite"));
        ov2.hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("OverlappedRead"));
        // 打开或创建文件
        hFile = CreateFileA(
            "MainFile.txt",
            GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_ALWAYS, // 文件不存在则创建
            FILE_FLAG_OVERLAPPED,
            NULL
        );
        if (hFile == INVALID_HANDLE_VALUE) {
            std::cout << "CreateFile failed, error: " << GetLastError() << std::endl;
        }
    }

    ~AsyncFileIO() {
        // 释放资源
        if (ov1.hEvent) CloseHandle(ov1.hEvent);
        if (ov2.hEvent) CloseHandle(ov2.hEvent);
        if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
    }

    // 启动异步写操作
    bool startWrite() {
        if (hFile == INVALID_HANDLE_VALUE) return false;

        DWORD promptLen = strlen(prompt1);
        // 设置写入位置到文件末尾
        LARGE_INTEGER fileSize;
        if (!GetFileSizeEx(hFile, &fileSize)) {
            std::cout << "GetFileSizeEx failed, error: " << GetLastError() << std::endl;
            return false;
        }
        ov1.Offset = fileSize.LowPart;
        ov1.OffsetHigh = fileSize.HighPart;

        DWORD bytesWritten = 0;
        if (!WriteFile(hFile, prompt1, promptLen, &bytesWritten, &ov1)) {
            DWORD err = GetLastError();
            if (err != ERROR_IO_PENDING) {
                std::cout << "WriteFile failed, error: " << err << std::endl;
                return false;
            }
        } else {
            ResetEvent(ov1.hEvent);
        }
        return true;
    }

    // 启动异步读操作
    bool startRead() {
        if (hFile == INVALID_HANDLE_VALUE) return false;

        // 设置读取位置到文件末尾前200字节
        LARGE_INTEGER fileSize;
        if (!GetFileSizeEx(hFile, &fileSize)) {
            std::cout << "GetFileSizeEx failed, error: " << GetLastError() << std::endl;
            return false;
        }
        LARGE_INTEGER readOffset;
        readOffset.QuadPart = max(fileSize.QuadPart - 200, 0LL);
        ov2.Offset = readOffset.LowPart;
        ov2.OffsetHigh = readOffset.HighPart;

        memset(chBuffer, 0, sizeof(chBuffer));
        DWORD bytesRead = 0;
        if (!ReadFile(hFile, chBuffer, dwRead, &bytesRead, &ov2)) {
            DWORD err = GetLastError();
            if (err != ERROR_IO_PENDING) {
                std::cout << "ReadFile failed, error: " << err << std::endl;
                return false;
            }
        } else {
            ResetEvent(ov2.hEvent);
        }
        return true;
    }

    // 等待操作完成并处理结果
    bool waitAndProcessIO() {
        if (hFile == INVALID_HANDLE_VALUE) return false;

        // 等待写操作完成
        DWORD waitResult = WaitForSingleObject(ov1.hEvent, dwTimeOut);
        if (waitResult == WAIT_FAILED) {
            std::cout << "Wait for write failed, error: " << GetLastError() << std::endl;
            return false;
        } else if (waitResult == WAIT_TIMEOUT) {
            std::cout << "Write operation timed out" << std::endl;
            return false;
        }

        // 获取写操作结果
        DWORD bytesWritten;
        if (!GetOverlappedResult(hFile, &ov1, &bytesWritten, FALSE)) {
            std::cout << "GetOverlappedResult for write failed, error: " << GetLastError() << std::endl;
            return false;
        }

        // 等待读操作完成
        waitResult = WaitForSingleObject(ov2.hEvent, dwTimeOut);
        if (waitResult == WAIT_FAILED) {
            std::cout << "Wait for read failed, error: " << GetLastError() << std::endl;
            return false;
        } else if (waitResult == WAIT_TIMEOUT) {
            std::cout << "Read operation timed out" << std::endl;
            return false;
        }

        // 获取读操作结果
        DWORD bytesRead;
        if (!GetOverlappedResult(hFile, &ov2, &bytesRead, FALSE)) {
            std::cout << "GetOverlappedResult for read failed, error: " << GetLastError() << std::endl;
            return false;
        }

        // 处理读取内容
        if (bytesRead > 0) {
            switch (chBuffer[0]) {
                case '1': std::cout << "option 1" << std::endl; break;
                case '2': std::cout << "option 2" << std::endl; break;
                case '3': std::cout << "exit" << std::endl; return false;
                default: std::cout << "option not valid!" << std::endl; break;
            }
        }

        return true;
    }

public:
    CHAR chBuffer[201]{};

private:
    LPCSTR prompt1{ "Option: " };
    DWORD dwRead{ 200 };
    DWORD dwTimeOut{ 5000 };

    HANDLE hFile;
    OVERLAPPED ov1{ 0 };
    OVERLAPPED ov2{ 0 };
};

int main() {
    AsyncFileIO io;

    do {
        if (!io.startWrite() || !io.startRead()) {
            break;
        }
    } while (io.waitAndProcessIO());

    return 0;
}

关键修复点

  • 修正字符串长度计算:使用strlen(prompt1)替代sizeof(prompt1),确保写入正确字节数。
  • 正确等待异步事件:等待OVERLAPPED结构中的事件句柄,而非文件句柄。
  • 添加结果验证:调用GetOverlappedResult获取实际读写字节数,确认操作成功。
  • 资源管理优化:构造函数初始化资源,析构函数释放,避免泄漏。
  • 指定读写位置:通过GetFileSizeEx设置OVERLAPPED的偏移量,确保读写位置正确。
  • 调整循环逻辑:先启动异步操作,等待完成后再处理数据,保证chBuffer就绪后执行switch判断。

内容的提问来源于stack exchange,提问作者RexyCode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 09:49:50