这是使用iocp读取文件的示例来源。
应该立即返回它,因为它在调用ReadFile时会进行异步调用,这似乎是同步工作的。
出什么问题了?
测试环境是Visual Studio 2017企业版,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;
}
答案 0 :(得分:1)
iocp 是绝对无关的。您尝试检查是否在异步文件句柄上立即返回或阻止I / O操作(以您的情况读取)。这里的
然后让我们看起来像-如何测试-被阻止还是返回代码?您根本不会测试。在ReadFile
返回之后测试此需求-操作是否完成。 I / O操作(ReadFile)是异步完成的-如果api调用已将控制权返回给您,但是OVERLAPPED
(IO_STATUS_BLOCK
)尚未由系统更新,则意味着I / O仍未完成。已OVERLAPPED
更新了,我们可以直接检查(Internal
结构的OVERLAPPED
成员是不是 STATUS_PENDING
)或通过调用GetOverlappedResult
与 bWait 设置为 FALSE
如果此参数为
FALSE
,并且操作仍在进行中,则 函数返回FALSE
,而GetLastError
函数返回ERROR_IO_INCOMPLETE
。
所以我们可以说ReadFile
是异步的,如果下4个编码为真:
ReadFile
返回FALSE
GetLastError()
返回ERROR_IO_PENDING
GetOverlappedResult(.., FALSE)
返回FALSE
GetLastError()
返回ERROR_IO_INCOMPLETE
您未在自己的代码中对此进行检查。相反,您要等到GetOverlappedResult(.., true)
的io操作完全完成,然后再花点时间。这有什么意义?
要测试的真实代码:
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%)查看“ sync(#2)”