关于ReadFile异步操作的问题以及我对同步操作和异步操作的理解,求指正

时间:2022-05-22 05:47:35
最近在看信号量之类的东西,感觉很有意思。下面说说我对同步操作和异步操作的理解,希望你们多指正。

同步操作是一个序列化的操作,必须一个操作完成之后才能进行下面的操作,可以说是有前后制约关系的;同步操作的缺点也是明显的,就是当遇到一个非常耗时的操作的时候,下面的操作就得等,这样对用户不太友好。

异步操作是各个操作之间是独立的,并没有牵制关系,类似于多线程之间的关系吧,异步操作的缺点也是明显的,就是容易引起混乱,为了避免混乱,引入了类似信号量 事件这种东西来实现异步操作之间的同步。

以上就是我对两个概念的理解,如果错误的地方请指正。

下面的代码中我用到了ReadFile的异步操作,用于从c盘读取一个大小为1GB名123.rar的文件,然后写入到D:\456.rar,我预期的效果是ReadFile操作之后马上返回,然后printf,然后CreateFile,毕竟printf和CreateFile这样的操作多简单,耗时又少,然后程序在io操作之间和WaitForSingleObject之间切换。可实际的效果是程序运行很长时间之后差不多快把文件读取完了,才printf出来,感觉ReadFile异步操作实际运行效果和ReadFile的同步操作效果一样呢?求解释。  


下面是代码

// waritfor.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
#include "stdlib.h"
int main(int argc, char* argv[])
{

//创建一个事件用于同步操作,初始化状态为没有信号
HANDLE hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);


if(NULL==hEvent)
{
printf("create event error");
return 0;

}

//打开一个文件
char FileName[]="c:\\123.rar";
HANDLE hFile = CreateFile(FileName,GENERIC_READ|FILE_FLAG_OVERLAPPED,0,NULL,OPEN_EXISTING,NULL,NULL);
if(NULL==hFile||INVALID_HANDLE_VALUE==hFile)
{
printf("open file error");
return 0;
}

//获取文件大小
DWORD size = GetFileSize(hFile,NULL);
if(NULL==size)
{

printf("get file size error");
return 0;
}

//以文件大小分配一块内存
LPVOID buf = (LPVOID)malloc(size);
if(NULL==buf)
{
printf("alloc mem error");
return 0;
}


//把文件的内容读取到内存中,由于是一个耗时较长的操作 这里采用异步读取数据 一个1GB的文件

OVERLAPPED ov;
ov.hEvent = hEvent;
ov.Offset = 0;
ov.OffsetHigh = 0;

BOOL bread = ReadFile(hFile,buf,size,NULL,&ov);
if(FALSE==bread)
{
printf("read file error %d");
return 0;
}

//问题在这里,既然这里的ReadFile是异步操作,ReadFile后会马上返回,然后printf,
//可是实际的测试效果感觉是把文件完整的读取进来之后才printf的,效果和同步操作一样,
//为什么呢? 搞不懂。
printf("正在读取文件");




//创建一个文件,当读取操作完成之后把数据写入到这个文件中,

char newfile[] = "d:\\456.rar";
HANDLE hnew = CreateFile(newfile,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,NULL,NULL);
if(NULL==hnew||INVALID_HANDLE_VALUE==hnew)
{
printf("create file error");
return 0;
}


//检测事件的是否有信号,如果有信号,说明读取文件操作完成了,开始写文件
DWORD wait = WaitForSingleObject(hEvent,INFINITE);

DWORD byteswritten;
if(WAIT_OBJECT_0==wait)//读取文件操作已经完成了
{

printf("读取文件完成,正在写入");
WriteFile(hnew,buf,size,&byteswritten,NULL);
if(byteswritten==size)
{
printf("写入文件成功");
return 1;
}
}

return 0;
}

11 个解决方案

#1


时间是花在了malloc上,而不是ReadFile上

Overlapped实际上是系统内部线程帮你做IO,信号量是用来同步的和IO没有必然联系

另外,如果异步file,建议用ReadFileEx

#2


文件异步操作,要在CreateFile倒数第2个参数dwFlagsAndAttributes设置FILE_FLAG_OVERLAPPED标志的吧!
不过就算是异步读取,你把放在同一线程里读写,效率改善是很有限的,因为也是要等读完才能写,可以考虑用多线程,分隔若干段进行读写。

#3


引用 2 楼  的回复:
文件异步操作,要在CreateFile倒数第2个参数dwFlagsAndAttributes设置FILE_FLAG_OVERLAPPED标志的吧!
不过就算是异步读取,你把放在同一线程里读写,效率改善是很有限的,因为也是要等读完才能写,可以考虑用多线程,分隔若干段进行读写。


额,FILE_FLAG_OVERLAPPED确实是倒数第二个参数的,感谢指出。

#4


忘记CloseHandle了。

#5


引用 1 楼  的回复:
时间是花在了malloc上,而不是ReadFile上

Overlapped实际上是系统内部线程帮你做IO,信号量是用来同步的和IO没有必然联系

另外,如果异步file,建议用ReadFileEx


谢谢,我再试试。

#6


这种大型的文件一次读入内存够折腾的,机器 受得了吗?

#7


引用 6 楼  的回复:
这种大型的文件一次读入内存够折腾的,机器 受得了吗?


恩,却是应该分几次读取。谢谢指正。

#8


if(FALSE==bread)
    {
        printf("read file error %d");
        return 0;
    }    
     
这里不能返回,因为是异步操作,如果 read false 时,getlasterror 如果为 ERROR_IO_PENDING,也是正常的

#9


这个程序应该不会有效果的。楼主对异步的理解错误。

#10


1. 这里不能用术语“序列化”,应该说,同步I/O是串行化操作。

2. 无论是同步还是异步I/O,你要明白,总是需要消耗一定的时间,而是对一次I/O而言,他们二者消耗的时间是基本一样的。读某种硬盘一次,就是需要那么多时间,硬盘的参数都有说明的,用硬盘的转数表示。异步操作也不能让硬盘加速旋转。不止对硬盘I/O,对任何外设都是一样,如网卡、U盘、游戏手柄等等,都一样,无论同步还是异步I/O消耗的时间是基本一样的。

3. 异步I/O的好处是,不阻塞线程。所以,我们可以在有限数量的线程里,处理很多很多的I/O操作。而同步I/O,一旦与外设操作,就会让当前线程陷入内核,直到操作结束,这样,我们在有限数量的线程里,在一定的时间内,所处理的I/O就会很少。

#11


异步I/O的基本基础是,CPU的速度,要远超过I/O的速度。

所以,我们可以让CPU在I/O操作完成前,做更多的事情。

#1


时间是花在了malloc上,而不是ReadFile上

Overlapped实际上是系统内部线程帮你做IO,信号量是用来同步的和IO没有必然联系

另外,如果异步file,建议用ReadFileEx

#2


文件异步操作,要在CreateFile倒数第2个参数dwFlagsAndAttributes设置FILE_FLAG_OVERLAPPED标志的吧!
不过就算是异步读取,你把放在同一线程里读写,效率改善是很有限的,因为也是要等读完才能写,可以考虑用多线程,分隔若干段进行读写。

#3


引用 2 楼  的回复:
文件异步操作,要在CreateFile倒数第2个参数dwFlagsAndAttributes设置FILE_FLAG_OVERLAPPED标志的吧!
不过就算是异步读取,你把放在同一线程里读写,效率改善是很有限的,因为也是要等读完才能写,可以考虑用多线程,分隔若干段进行读写。


额,FILE_FLAG_OVERLAPPED确实是倒数第二个参数的,感谢指出。

#4


忘记CloseHandle了。

#5


引用 1 楼  的回复:
时间是花在了malloc上,而不是ReadFile上

Overlapped实际上是系统内部线程帮你做IO,信号量是用来同步的和IO没有必然联系

另外,如果异步file,建议用ReadFileEx


谢谢,我再试试。

#6


这种大型的文件一次读入内存够折腾的,机器 受得了吗?

#7


引用 6 楼  的回复:
这种大型的文件一次读入内存够折腾的,机器 受得了吗?


恩,却是应该分几次读取。谢谢指正。

#8


if(FALSE==bread)
    {
        printf("read file error %d");
        return 0;
    }    
     
这里不能返回,因为是异步操作,如果 read false 时,getlasterror 如果为 ERROR_IO_PENDING,也是正常的

#9


这个程序应该不会有效果的。楼主对异步的理解错误。

#10


1. 这里不能用术语“序列化”,应该说,同步I/O是串行化操作。

2. 无论是同步还是异步I/O,你要明白,总是需要消耗一定的时间,而是对一次I/O而言,他们二者消耗的时间是基本一样的。读某种硬盘一次,就是需要那么多时间,硬盘的参数都有说明的,用硬盘的转数表示。异步操作也不能让硬盘加速旋转。不止对硬盘I/O,对任何外设都是一样,如网卡、U盘、游戏手柄等等,都一样,无论同步还是异步I/O消耗的时间是基本一样的。

3. 异步I/O的好处是,不阻塞线程。所以,我们可以在有限数量的线程里,处理很多很多的I/O操作。而同步I/O,一旦与外设操作,就会让当前线程陷入内核,直到操作结束,这样,我们在有限数量的线程里,在一定的时间内,所处理的I/O就会很少。

#11


异步I/O的基本基础是,CPU的速度,要远超过I/O的速度。

所以,我们可以让CPU在I/O操作完成前,做更多的事情。