要使用异步功能首先需要在打开文件时指定FILE_FLAG_OVERLAPPED作为标记(dwFlagsAndAttributes),在读写文件时可以使用ReadFile/WriteFile或者ReadFileEx /WriteFileEx来进行读写,当调用不同的函数时读写完成后通知应用程序的方法有所不同的,。下面分别介绍这两种方法:
第一种方法,利用ReadFile/WriteFile,这对函数使用事件信号来进行读写完成的通知,由于磁盘读写是一个比较耗费时间的操作,而且现在的磁盘子系统可以在磁盘读写时脱离CPU而单独进行,例如使用DMA方式,所以在磁盘读写时可以进行其他一些操作以充分利用CPU。关于事件信号相关内容请参考。并且在文件读写时提供OVERLAPPED数据。
结构定义如下: typedef struct _OVERLAPPED { ULONG_PTR Internal; //系统使用 ULONG_PTR InternalHigh; //系统使用 DWORD Offset; // 文件读或写的开始位置低32位,对于命名管道和其他通信设备必须设置为0 DWORD OffsetHigh; // 文件读或写的开始位置高32位,对于命名管道和其他通信设备必须设置为0 HANDLE hEvent; // 事件量,当操作完成时,这个时间会变为有信号状态 } OVERLAPPED; //下面的代码演示了文件异步读取 //并且比较了同步和异步之间的性能差异 void DoDataDeal(BYTE *pbData,int iLen) {//对字节进行操作 Sleep(3*1000);//假设每次计算需要2秒钟 } //下面是使用异步读的示范代码,假设c:/temp/large_file.dat文件有130MB大小() //每次读入10MB字节,并且对文件中每个字节进行操作,由于可以使用异步操作所以可以在下一次读入数据的同时进行计算 void ReadM1(void) { HANDLE hFile = CreateFile("c://temp//large_file.dat",GENERIC_READ,0, NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL,NULL); if( INVALID_HANDLE_VALUE != hFile ) { HANDLE hEvent = CreateEvent(NULL,FALSE,FALSE,"read_event"); BYTE *pbRead = new BYTE[1024*1024*10];//10MB字节 BYTE *pbBuf = new BYTE[1024*1024*10]; DWORD dwRead,dwCount=0; OVERLAPPED overlap; overlap.Offset = 0; overlap.OffsetHigh =0; overlap.hEvent = hEvent; DWORD dwBegin= GetTickCount();//记录开始时间 ReadFile(hFile,pbRead,1024*1024*10,&dwRead,&overlap); {//开始计算 for(int i=1;i<13;i++) { printf("M1 i=%d/n",i); WaitForSingleObject(hEvent,INFINITE);//等待上一次完成 memcpy(pbBuf,pbRead,1024*1024*10); overlap.Offset = i * (1024*1024*10); overlap.OffsetHigh =0; overlap.hEvent = hEvent; ReadFile(hFile,pbRead,1024*1024*10,&dwRead,&overlap); //在文件进行读的同时进行计算 DoDataDeal(pbBuf,1024*1024*10); } WaitForSingleObject(hEvent,INFINITE);//等待最后一次完成 memcpy(pbBuf,pbRead,1024*1024*10); //数据处理 DoDataDeal(pbBuf,1024*1024*10); }//结束计算 printf("耗时 %d/n",GetTickCount()-dwBegin); //操作完成 CloseHandle(hEvent); CloseHandle(hFile); delete pbRead; delete pbBuf; } } //下面是上面功能的文件同步读版本 void ReadM2(void) { HANDLE hFile = CreateFile("c://temp//large_file.dat",GENERIC_READ,0, NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if( INVALID_HANDLE_VALUE != hFile ) { DWORD dwRead,dwCount=0; BYTE *pbRead = new BYTE[1024*1024*10];//10MB字节 BYTE *pbBuf = new BYTE[1024*1024*10];//10MB字节 DWORD dwBegin= GetTickCount();//记录开始时间 for(int i=0;i<13;i++) { printf("M2 i=%d/n",i); if(!ReadFile(hFile,pbRead,1024*1024*10,&dwRead,NULL)) { printf("error read"); break; } memcpy(pbBuf,pbRead,1024*1024*10); //计算 DoDataDeal(pbBuf,1024*1024*10); } printf("耗时 %d/n",GetTickCount()-dwBegin); //操作完成 CloseHandle(hFile); delete pbRead; delete pbBuf; } }在文件的异步读写中,如果ReadFile/WriteFile返回FALSE,则需要通过LastError来进一步确认是否发生错误,例如下面的错误检测代码:
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, &gOverlapped) ; if (!bResult) switch (dwError = GetLastError()) { case ERROR_HANDLE_EOF: { //到达文件尾 } case ERROR_IO_PENDING: { //正在进行异步操作 } // end case } // end switch } // end if 此外可以通过GetOverlappedResult函数来得到异步函数的执行情况 BOOL GetOverlappedResult( HANDLE hFile, // handle to file, pipe, or device LPOVERLAPPED lpOverlapped, // overlapped structure LPDWORD lpNumberOfBytesTransferred, // bytes transferred BOOL bWait // wait option );如果函数调用返回FALSE则可以用GetLastError来得到错误,如果返回成功则可以通过lpNumberOfBytesTransferred 参数来确定当前有多少数据已经被读或写。lpOverlapped参数必须与调用ReadFile或WriteFile时使用同一个数据区。最后一个参数 bWait表明是否等待异步操作结束时才返回,如果设置为TRUE就可以等待文件读写完成时返回,否则就会马上返回,利用这个特点可以利用它来等待异步文件操作的结束(就如同等待事件变为有信号状态一样起到相同的作用)。