C 创建一个异步;ReadFileMany();在ReadFile()之上
我正在尝试创建一个函数,C 创建一个异步;ReadFileMany();在ReadFile()之上,c,winapi,visual-c++,readfile,C,Winapi,Visual C++,Readfile,我正在尝试创建一个函数,ReadFileMany,它模拟了ReadFile的接口,该接口提供了一个要读取的(偏移量、长度)对列表,并异步读取文件的所有部分 我在ReadFileMany中这样做,使用RegisterWaitForSingleObject来指示线程池线程等待I/O完成,以便它可以再次调用ReadFile来读取文件的下一部分,等等 我遇到的问题是,我似乎无法模仿ReadFile的特定行为 具体而言,文件句柄本身可以像事件一样使用,而不需要事件句柄: OVERLAPPED ov = {
ReadFileMany
,它模拟了ReadFile
的接口,该接口提供了一个要读取的(偏移量、长度)对列表,并异步读取文件的所有部分
我在ReadFileMany
中这样做,使用RegisterWaitForSingleObject
来指示线程池线程等待I/O完成,以便它可以再次调用ReadFile
来读取文件的下一部分,等等
我遇到的问题是,我似乎无法模仿ReadFile
的特定行为具体而言,文件句柄本身可以像事件一样使用,而不需要事件句柄:
OVERLAPPED ov = {0};
DWORD nw;
if (ReadFile(hFile, buf, buf_size, &nw, &ov))
{
if (WaitForSingleObject(hFile, INFINITE) == WAIT_OBJECT_0)
{
...
}
}
但当然,如果用户等待文件句柄,他可能会收到一个通知,说明中间读取已完成,而不是最终读取
因此,在ReadFileMany
中,除了最后一部分之外,我别无选择,只能将hEvent
参数传递给ReadFile
问题是,当所有部分都已读取时,是否仍有办法允许用户等待文件句柄发出信号?
一开始答案似乎很明显:阅读最后一部分时,请避免传递事件句柄如果读取成功,那么这种方法可以正常工作,但如果出现错误,则不行。如果
ReadFile
在最后一次读取时突然返回错误,我需要手动将文件句柄设置为信号状态,以允许读取器从对WaitForSingleObject(hFile)
的潜在调用中醒来
但如果不执行实际I/O,似乎无法将文件句柄设置为信号状态。。。这就是我陷入困境的地方:我有没有办法编写这个函数,让它在外部像
ReadFile
一样运行,或者不可能正确地执行它?假设您试图读取单个文件的多个部分,我只需分配一个重叠的结构数组,对于文件的每个请求部分,一次启动所有ReadFile()
调用,然后使用WaitForMultipleObjects()
等待所有I/O完成,例如:
struct sReadInfo
{
OVERLAPPED ov;
LPVOID pBuffer;
DWORD dwNumBytes;
DWORD dwNumBytesRead;
bool bPending;
sReadInfo()
{
memset(&ov, 0, sizeof(OVERLAPPED));
pBuffer = NULL;
dwNumBytes = 0;
dwNumBytesRead = 0;
bPending = false;
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ov.hEvent) throw std::exception();
}
~sReadInfo()
{
CloseHandle(hEvent);
}
};
bool error=false;
尝试
{
std::向量ri(numSections);
std::载体h;
h、 储备(numSections);
对于(int i=0;iov.Offset=ul.LowPart;
r->ov.OFFETHIGH=ul.HighPart;
r->pBuffer=…;//需要读入的缓冲区
r->dwNumBytes=…;//需要读取的字节数
如果(!ReadFile(hFile,r->pBuffer,r->dwNumBytes,&r->dwNumBytes读取,&r->ov))
{
如果(GetLastError()!=错误\u IO\u挂起)
抛出std::exception();
r->bPending=true;
h、 向后推(r->ov.hEvent);
}
}
如果(!h.empty())
{
if(WaitForMultipleObjects(h.size(),&h[0],TRUE,无穷大)!=WAIT\u OBJECT\u 0)
抛出std::exception();
}
对于(int i=0;iB结束)
GetOverlappedResult(hFile,&r->ov,&r->DWnumbytes读取,FALSE);
// ...
}
}
捕获(const std::exception&)
{
取消(hFile);
返回false;
}
返回true;
您从哪里想到可以将文件句柄传递给WaitForSingleObject()
?文件句柄不是可等待的对象,所以你不能等待它。这就是你要写的吗?@Damon:不;事实上,恰恰相反。ReadFileScatter
中的“分散”指的是内存缓冲区(内存中分散的页面),而ReadFileMemory
中的“多”指的是文件的不同部分。@RemyLebeau:是的,实际上可以。(试试看。)显然,ReadFile
文档似乎没有提到它,但是如果你看一下,你会发现这是完全正确的,文件句柄可以像事件句柄一样发出信号:“通过等待文件句柄,调用线程可以同步到读取操作的完成。每次在句柄上发出的I/O操作完成时,都会向句柄发出信号。“@Mehrdad:根据该文档,仅当文件以SYNCHRONIZE
访问权限打开时,并且仅当一次只执行一个I/O操作时,它才起作用。应使用ZwWaitForSingleObject()完成等待
。CreateFile()
、ReadFile()
或WaitFor…()
文档都没有提到任何关于文件句柄作为可等待对象被支持的内容,所以我真的不会依赖于这种行为。ZwReadFile()
是比ReadFile()更低级别的API
,所以不要将较低级别和较高级别的API语义混合在一起。有趣的是……这不会在磁盘上造成很大的震动吗?用户如何知道所有读取都已完成,而不是仅完成其中一个读取?似乎我们仍然存在与以前相同的问题。它仍然是重叠的I/O,因此Windows可以安排实际的读取视情况而定的光盘活动。如果仔细观察,我将设置WaitForMultipleObjects()的bWaitAll
参数
设置为TRUE,因此在所有请求的读取完成之前它不会退出。看起来您完全错过了问题,这与等待文件句柄有关。您正在等待事件句柄,这已经是我在问题中提到的,但它没有为问题提供与我之前提到的相同的接口仔细阅读问题,了解问题的实质。
bool error = false;
try
{
std::vector<sReadInfo> ri(numSections);
std::vector<HANDLE> h;
h.reserve(numSections);
for(int i = 0; i < numSections; ++i)
{
ULARGE_INTEGER ul;
ul.QuadPart = ...; // desired file offset to read from
sReadInfo *r = &ri[i];
r->ov.Offset = ul.LowPart;
r->ov.OffsetHigh = ul.HighPart;
r->pBuffer = ...; // desired buffer to read into
r->dwNumBytes = ...; // desired number of bytes to read
if (!ReadFile(hFile, r->pBuffer, r->dwNumBytes, &r->dwNumBytesRead, &r->ov))
{
if (GetLastError() != ERROR_IO_PENDING)
throw std::exception();
r->bPending = true;
h.push_back(r->ov.hEvent);
}
}
if (!h.empty())
{
if (WaitForMultipleObjects(h.size(), &h[0], TRUE, INFINITE) != WAIT_OBJECT_0)
throw std::exception();
}
for (int i = 0; i < numSections; ++i)
{
sReadInfo *r = &ri[i];
if (r->bPending)
GetOverlappedResult(hFile, &r->ov, &r->dwNumBytesRead, FALSE);
// ...
}
}
catch (const std::exception &)
{
CancelIo(hFile);
return false;
}
return true;