readfile - IOCP ReadFile 总是阻塞直到读取完成
问题描述
这是使用 iocp 读取文件的示例源。
它应该立即返回,因为它在调用 ReadFile 时进行了异步调用,这似乎是同步工作的。
问题是什么?
测试环境为visual studio 2017 enterprise,windwos 10,windows sdk版本为10.0.17763.0。
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
const int BUFFERSIZE = 1024 * 1024 * 400;
BYTE ReadBuffer[BUFFERSIZE] = { 0 };
DWORD WINAPI WaitQueue(LPVOID lpParam)
{
auto hIocp = (HANDLE)lpParam;
// WAIT COMPLETION QUEUE
DWORD numberOfBytes;
ULONG_PTR val;
LPOVERLAPPED ov = { 0 };
for (;;)
{
BOOL bSuccess = GetQueuedCompletionStatus(hIocp, &numberOfBytes, (PULONG_PTR)&val, &ov, INFINITE);
SYSTEMTIME dequeTime;
GetSystemTime(&dequeTime);
Sleep(1000);
printf("dequeue time %dsec %dmilli", dequeTime.wSecond, dequeTime.wMilliseconds);
}
}
DWORD WINAPI ReadFileThread(LPVOID lpParam)
{
Sleep(3000);
auto hIocp = (HANDLE)lpParam;
// CREATE FILE HANDLE
auto fileName = "e:\\test.msi";
auto hFile = CreateFile(fileName,
FILE_READ_DATA,
FILE_SHARE_READ,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (hFile == INVALID_HANDLE_VALUE)
{
std::wcout << L"create file fail - " << fileName << std::endl;
return 0;
}
// REGIST FILE HANDLE TO IOCP
if (hIocp != CreateIoCompletionPort(hFile, hIocp, 0, 2))
{
auto err = GetLastError();
std::cout << "add file handle fail:" << err << " - file handle:" << hIocp << std::endl;
CloseHandle(hFile);
CloseHandle(hIocp);
return 0;
}
// READ FILE
OVERLAPPED ol = { 0 };
SYSTEMTIME startTime;
GetSystemTime(&startTime);
if (FALSE == ReadFile(hFile, ReadBuffer, _countof(ReadBuffer), 0, &ol))
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("Terminal failure: Unable to read from file.\n GetLastError=%08x\n", GetLastError());
CloseHandle(hFile);
return -1;
}
}
DWORD d;
GetOverlappedResult(hFile, &ol, &d, true);
SYSTEMTIME endTime;
GetSystemTime(&endTime);
printf("start time %dsec %dmilli", startTime.wSecond, startTime.wMilliseconds);
printf("end time %dsec %dmilli", endTime.wSecond, endTime.wMilliseconds);
}
int main()
{
// CREATE ICOP
auto hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
if (hIocp == NULL)
{
auto err = GetLastError();
std::cout << "Create IOCP failed. error:" << err << std::endl;
return 0;
}
// CREATE READ THREAD
CreateThread(
NULL,
0,
ReadFileThread,
hIocp,
0,
nullptr
);
// CREATE WAIT DEQUEUE THREAD
CreateThread(
NULL,
0,
WaitQueue,
hIocp,
0,
nullptr
);
while (true)
{
}
return 0;
}
解决方案
首先,这里的iocp是绝对不相关的。您尝试检查异步文件句柄立即返回或阻塞上的 I/O 操作(在您的情况下为读取)。这里的iocp有什么关系?将iocp绑定到文件是在 I/O 完成时获得通知的唯一方法。但这绝对不会影响 I/O 本身阻塞或立即返回。我们可以在这里使用任何通知方式。(apc、iocp 或事件)。为了您的测试目的,最简单的使用事件。
然后让我们看看 - 你如何测试 - 在你的代码中被读取或返回?你根本不测试这个。返回后测试此需求ReadFile
- 操作是否完成。I/O 操作 (ReadFile) 是异步完成的 - 如果 api 调用已经将控制权返回给您,但是OVERLAPPED
( IO_STATUS_BLOCK
) 尚未被系统更新,这意味着 I/O 仍未完成。OVERLAPPED
更新我们可以直接检查(结构成员Internal
不是)或通过调用将bWait设置为FALSEOVERLAPPED
STATUS_PENDING
GetOverlappedResult
如果此参数为
FALSE
且操作仍处于挂起状态,则函数返回FALSE
且GetLastError
函数返回ERROR_IO_INCOMPLETE
。
所以我们可以说ReadFile
如果接下来的 4 个代码为真,则异步完成:
ReadFile
返回FALSE
GetLastError()
返回ERROR_IO_PENDING
GetOverlappedResult(.., FALSE)
返回FALSE
GetLastError()
返回ERROR_IO_INCOMPLETE
你没有在自我代码中检查这个。相反,您要等到 io 操作完全完成,GetOverlappedResult(.., true)
然后花点时间。这有什么意义?
实际测试代码:
void tt(PCWSTR FileName)
{
HANDLE hFile = CreateFile(FileName, FILE_GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED ov {};
if (ov.hEvent = CreateEvent(0, 0, 0, 0))
{
char buf[1024];
if (ReadFile(hFile, buf, sizeof(buf), 0, &ov))
{
DbgPrint("sync(#1)\n");
}
else
{
switch (GetLastError())
{
case ERROR_IO_PENDING:
ULONG n;
if (GetOverlappedResult(hFile, &ov, &n, FALSE))
{
DbgPrint("sync(#2)\n");
}
else
{
switch (GetLastError())
{
case ERROR_IO_INCOMPLETE:
DbgPrint("async\n");
if (!GetOverlappedResult(hFile, &ov, &n, TRUE))
{
__debugbreak();
}
break;
default: __debugbreak();
}
}
break;
default: __debugbreak();
}
}
CloseHandle(ov.hEvent);
}
CloseHandle(hFile);
}
}
请注意,读取的结果(同步与否)取决于缓存中的文件数据。如果您为文件调用它,该文件之前未读取(因此数据不在缓存中)可能的 api 打印“异步”,但如果您再次为同一个文件调用此函数 - 您下次更快(几乎 100%)将查看“同步(#2)”
推荐阅读
- javascript - 滚动到顶部时,ScrollTop 不会恢复
- r - R中的键值对数据结构,其中键长度大于1(每个值多个键)
- python - 在 post 方法中访问动态 url 的任何直接方法
- c++ - OpenGL 程序编译但不能正确启动
- c# - 在 C# 中使用带有 NUNIT 的 params 关键字提供的参数过多
- python - SessionNotCreatedException:消息:未从选项卡创建的会话在 Python 中使用 Odoo 崩溃,但 selenium 单独工作正常
- python - 如何在 Maya 中查询反转和重叠 UV 的数量
- php - 从另一个类调用方法时出错
- c# - 对 jpeg 文件使用 JpegBitmapDecoder.Create() 时出现意外的属性类型或值
- php - 我想访问 StdClass 对象的内部值