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

