如何在c++中异步读取文件
How to read files asynchroneusly in c++?
我需要异步读取文件
string read(string path) {
DWORD readenByte;
int t;
char* buffer = new char[512];
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, "read");
OVERLAPPED overlap;
overlap.hEvent = hEvent;
HANDLE hFile = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(!hFile) {
Debug::error(GetLastError(), "fileAsync.cpp::read - ");
}
t = ReadFile(hFile, buffer, MAX_READ - 1, &readenByte, &overlap);
if(!t) {
Debug::error(GetLastError(), "fileAsync.cpp::read - ");
}
t = WaitForSingleObject(hEvent, 5000);
if(t == WAIT_TIMEOUT) {
Debug::error("fail to read - timeout, fileAsync.cpp::read");
}
buffer[readenByte] = ' ';
string str = buffer;
return str;
}
我在ReadFile - 38中遇到错误:到达文件的末尾
如何在c++中使用winapi异步读取文件?
你的代码中有几个bug需要解决,有些会导致失败,有些会导致灾难性的失败。
-
第一个错误导致你得到的错误代码:你有一个未初始化的OVERLAPPED结构,指示下面的
ReadFile
调用从存储在Offset和OffsetHigh成员中的随机文件位置读取。要解决这个问题,初始化数据:OVERLAPPED overlap = {0};
. -
接下来,您不打开文件以进行异步访问。随后从文件异步读取,您需要调用CreateFile传递
FILE_FLAG_OVERLAPPED
为dwFlagsAndAttributes。如果你不这样做,你就得花几个月的时间去寻找一个bug(参见如果你忘记在异步句柄上传递OVERLAPPED结构会发生什么?)。 -
ReadFile的文档解释说,lpNumberOfBytesRead参数不用于异步I/O,你应该传递
NULL
代替。这应该是显而易见的,因为在知道传输的字节数之前,异步ReadFile
调用返回。要获取传输负载的大小,在异步I/O完成后调用GetOverlappedResult。 -
下一个bug只会导致内存泄漏。您动态地分配
buffer
,但从不调用delete[] buffer;
。要么删除缓冲区,要么分配一个具有自动存储时间的缓冲区(char buffer[MAX_READ] = {0};
),或者使用c++容器(例如std::vector<char> buffer(MAX_READ);
)。 -
另一个错误是,当您尝试从
buffer
构造std::string
时:您选择的构造函数不能处理嵌入的NUL
字符。它只会截断你得到的。您需要调用带有显式长度参数的std::string构造函数。但即使这样,如果文件的字符编码与std::string
不一致,您也可能以垃圾告终。 -
最后,发出一个异步读,后面跟着
WaitForSingleObject
,本质上是一个同步读,不买任何东西。我假设这只是用于测试,而不是您的最终代码。在完成此操作时,请记住,只要异步读操作正在运行,OVERLAPPED
结构就需要保持存活。
不能立即解决错误的其他建议:
-
你正在传递一个
std::string
给你的read
函数,在CreateFile
调用中使用。Windows始终使用UTF-16LE编码,当使用Visual Studio(以及可能的其他Windows编译器)时,它映射到wchar_t
/std::wstring
。传递std::string
/const char*
有两个直接的缺点:- 调用ANSI API导致字符串从MBCS转换为UTF-16(反之亦然)。这既不必要地浪费资源,又以非常微妙的方式失败,因为它依赖于当前的区域设置。 不是每个Unicode码点都可以用MBCS编码表示。这意味着,当使用MBCS字符编码时,某些文件无法打开。
-
您正在创建一个只能通过其
HANDLE
值访问的事件对象,而不是通过其名称。您可以将NULL
作为lpName参数传递给CreateEvent。这可以防止潜在的名称冲突,对于像"read"
这样通用的名称,这一点尤为重要。
始终使用Unicode API (CreateFileW
)和UTF-16字符串(std::wstring
/wchar_t
)。还可以在编译器的命令行中定义预处理器符号UNICODE
(用于Windows API)和_UNICODE
(用于CRT),以避免意外调用任何ANSI API。
1)您需要在CreateFile调用的第6个参数(dwFlagsAndAttributes
)中包含标志FILE_FLAG_OVERLAPPED
。这就是为什么重叠读很可能失败的原因。
2) MAX_READ
的值是多少?我希望它小于513,否则如果文件大于512字节,就会发生不好的事情。
3) ReadFile
与重叠的结构指针不是NULL
将给你错误码997 (ERROR_IO_PENDING
),这是预期的,因此你不能在调用ReadFile
后计算t
。
4)在异步操作的情况下,ReadFile
函数不存储你在调用中传递的指针中读取的字节,你必须在操作完成后自己查询重叠的结果
这是一个小的工作片段,我希望你能从中构建:
#include <Windows.h>
#include <iostream>
#include <sstream>
class COverlappedCompletionEvent : public OVERLAPPED
{
public:
COverlappedCompletionEvent() : m_hEvent(NULL)
{
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
auto nError = GetLastError();
std::stringstream ErrorStream;
ErrorStream << "CreateEvent() failed with " << nError;
throw std::runtime_error(ErrorStream.str());
}
ZeroMemory(this, sizeof(OVERLAPPED));
hEvent = m_hEvent;
}
~COverlappedCompletionEvent()
{
if (m_hEvent != NULL)
{
CloseHandle(m_hEvent);
}
}
private:
HANDLE m_hEvent;
};
int main(int argc, char** argv)
{
try
{
if (argc != 2)
{
std::stringstream ErrorStream;
ErrorStream << "usage: " << argv[0] << " <filename>";
throw std::runtime_error(ErrorStream.str());
}
COverlappedCompletionEvent OverlappedCompletionEvent;
char pBuffer[512];
auto hFile = CreateFileA(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (hFile == NULL)
{
auto nError = GetLastError();
std::stringstream ErrorStream;
ErrorStream << "CreateFileA() failed with " << nError;
throw std::runtime_error(ErrorStream.str());
}
if (ReadFile(hFile, pBuffer, sizeof(pBuffer), nullptr, &OverlappedCompletionEvent) == FALSE)
{
auto nError = GetLastError();
if (nError != ERROR_IO_PENDING)
{
std::stringstream ErrorStream;
ErrorStream << "ReadFile() failed with " << nError;
throw std::runtime_error(ErrorStream.str());
}
}
::WaitForSingleObject(OverlappedCompletionEvent.hEvent, INFINITE);
DWORD nBytesRead = 0;
if (GetOverlappedResult(hFile, &OverlappedCompletionEvent, &nBytesRead, FALSE))
{
std::cout << "Read " << nBytesRead << " bytes" << std::endl;
}
CloseHandle(hFile);
}
catch (const std::exception& Exception)
{
std::cout << Exception.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
- 带有 Boost.Beast 的异步读取标头
- TCP 服务器的异步读取使用 boost::asio 打印客户端套接字发送的数据
- 增强 ASIO 和串行端口异步读取
- 如何使用软化工具包从 OPC UA 服务器异步读取操作回调中的数据值响应中获取 NodeId 详细信息
- C++ / RS232C 串口 / 事件回调异步读取缓冲区 / 不使用线程
- XAudio2 教程 - 单独的线程和异步读取?
- 如何在读取/写入文件的同时异步读取/写入Beast websocket
- 如何阻止 boost::asio 异步读取混淆
- 使用Qt从stdin异步读取
- 异步读取完成,但缓冲区没有包含预期的结果
- 如何在Windows中使用boost asio从命令行异步读取输入
- 从Poco异步读取HTTPClientSession
- 我怎样才能得到异步读取上传输的字节量
- 内存尖峰-提高ASIO异步读取
- boost::asio acceptor在EOF后重新打开和异步读取
- 异步读取文件与boost asio
- C++ 提升 ASIO |同步写入到异步读取 |无法在第二次读取中接收正确的数据
- 使用 WINAPI 读取文件从文件异步读取行
- C++ Ubuntu select() 如果串行接口有异步读取的数据
- 如何使用 IMoniker 和 IStream 接口异步读取 Exchange 服务器