乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      磁盤文件的正常讀寫與異步讀寫

       昵稱9954619 2012-05-13
      磁盤文件的正常讀寫與異步讀寫

      在Win32系統(tǒng)下文件可以支持平常的同步讀寫和異步讀寫(但在Win9X下,Win32系統(tǒng)不支持磁盤文件的異步讀寫)。本節(jié)在后面部分將會介紹文件的異步讀寫,最后一段內容將向大家講解一下文件的區(qū)域加鎖

      在Win32系統(tǒng)中支持64位長度的文件,所以在很多文件操作函數中需要兩個DWORD參數來表示文件長度,一個DWORD用來表示低32位,另一個用來表示高32位。

      文件的讀寫進行在文件被正確打開后,但請確認在打開文件時設置了正確的讀寫標記。在Win32的文件操作中沒有了以前類似與以前ANSI C中的fputs fgets fprintf fscanf等函數,只有類似于fread和fwrite的ReadFile和WriteFile函數。

      ReadFile用于文件讀,函數原型為:

      BOOL ReadFile(
        HANDLE hFile,                // handle to file
        LPVOID lpBuffer,             // data buffer
        DWORD nNumberOfBytesToRead,  // number of bytes to read
        LPDWORD lpNumberOfBytesRead, // number of bytes read
        LPOVERLAPPED lpOverlapped    // overlapped buffer
      );
      
      其中各項參數的含義為:
      • hFile:文件句柄,為CreateFile時返回的句柄
      • lpBuffer:保存讀入的數據的指針
      • nNumberOfBytesToRead:指定需要讀入的字節(jié)數
      • lpNumberOfBytesRead:返回實際讀入的字節(jié)數
      • lpOverlapped:在文件異步讀寫時使用的數據,在同步讀寫中全部都設置為NULL,在Win9X中只支持對串口的異步操作。
      如果返回值為FALSE并且讀入的字節(jié)數也返回為0,則表示文件到達了末尾。
      WriteFile用于文件寫,函數原型為:
      BOOL WriteFile(
        HANDLE hFile,                    // handle to file
        LPCVOID lpBuffer,                // data buffer
        DWORD nNumberOfBytesToWrite,     // number of bytes to write
        LPDWORD lpNumberOfBytesWritten,  // number of bytes written
        LPOVERLAPPED lpOverlapped        // overlapped buffer
      );
      
      參數的含義和ReadFile類似。

      如果需要移動文件指針到相關位置(和文件讀寫不同,這個函數沒有異步版本),使用

      DWORD SetFilePointer(
        HANDLE hFile,                // handle to file
        LONG lDistanceToMove,        // bytes to move pointer
        PLONG lpDistanceToMoveHigh,  // bytes to move pointer
        DWORD dwMoveMethod           // starting point
      );
      
      其中各項參數的含義為:
      • hFile:文件句柄,為CreateFile時返回的句柄
      • lpBuffer:保存讀入的數據的指針
      • lDistanceToMove:移動的字節(jié)數低DWORD
      • lpDistanceToMoveHigh:移動的字節(jié)數高DWORD,為了支持64位(2的64次方字節(jié))長度的大文件,而用來指定64字節(jié)的高32位,如果文件大小只需要32位就可以表示,則設置為NULL
      • ldwMoveMethod:移動方法,可以選擇下面的值。
        FILE_BEGIN 從文件開始處開始移動
        FILE_CURRENT 從文件開始除開始移動
        FILE_END 從文件末尾開始移動
      函數返回值和參數lpDistanceToMoveHigh(當該參數不為NULL時)表明文件指針當前的位置(從文件頭到當前的字節(jié)數),但當參數lpDistanceToMoveHigh為NULL時如果返回INVALID_SET_FILE_POINTER表明執(zhí)行失敗,當參數lpDistanceToMoveHigh不為NULL時如果返回INVALID_SET_FILE_POINTER還需要判斷GetLastError的返回值是否不為NO_ERROR。下面是兩種情況下判斷錯誤。
      //第一種情況
      DWORD dwPtr = SetFilePointer (hFile, lDistance, NULL, FILE_BEGIN) ; 
       
      if (dwPtr == INVALID_SET_FILE_POINTER) // Test for failure
      { 
          // Obtain the error code. 
          dwError = GetLastError() ; 
       
          // 處理錯誤
          // . . . 
       
      } // End of error handler 
      
      //第二種情況
      dwPtrLow = SetFilePointer (hFile, lDistLow, & lDistHigh, FILE_BEGIN) ; 
       
      // Test for failure
      if (dwPtrLow == INVALID_SET_FILE_POINTER && (dwError = GetLastError()) != NO_ERROR )
      { 
          // 處理錯誤 
          // . . . 
      
      } // End of error handler 
      
      在Win32中沒有提供得到文件當前指針位置的函數,但通過SetFilePointer也可以確定文件指針當前的位置。在MSDN中提供了兩個宏來得到當前文件的指針位置:
      //對32位長度的文件
          #define GetFilePointer(hFile) SetFilePointer(hFile, 0, NULL, FILE_CURRENT)
      //對超過32位長度的文件
          #define GetVLFilePointer(hFile, lpPositionHigh)         (*lpPositionHigh = 0,         SetFilePointer(hFile, 0, lpPositionHigh, FILE_CURRENT))
      
      對了可以通過SetFilePointer來得到文件長度,方法就是從文件位結束處移動0字節(jié),返回值就是文件的長度。
      HANDLE hFile = CreateFile(...);//打開文件進行讀
      DWORD dwLen;
      dwLen = SetFilePointer (hFile, 0, NULL, FILE_END) ; 
      CloseHandle( hFile ) ;
      
      當然Win32中也提供了專門的函數來得到文件的大小
      BOOL GetFileSizeEx(
        HANDLE hFile,              // handle to file
        PLARGE_INTEGER lpFileSize  // file size
      );
      typedef union _LARGE_INTEGER { 
        struct {
            DWORD LowPart; 
            LONG  HighPart; 
        };
        LONGLONG QuadPart;
      } LARGE_INTEGER, *PLARGE_INTEGER; 
      
      其中l(wèi)pFileSize是一個聯合數據,用來表明文件的大小。

      文件的異步讀寫主要是用在大文件的讀寫上,當使用異步讀寫時,ReadFile和WriteFile會馬上返回,并在讀寫完成時通知應用程序。

      要使用異步功能首先需要在打開文件時指定FILE_FLAG_OVERLAPPED作為標記(dwFlagsAndAttributes),在讀寫文件時可以使用ReadFile/WriteFile或者ReadFileEx/WriteFileEx來進行讀寫,當調用不同的函數時讀寫完成后通知應用程序的方法有所不同的,。下面分別介紹這兩種方法:

      第一種方法,利用ReadFile/WriteFile,這對函數使用事件信號來進行讀寫完成的通知,由于磁盤讀寫是一個比較耗費時間的操作,而且現在的磁盤子系統(tǒng)可以在磁盤讀寫時脫離CPU而單獨進行,例如使用DMA方式,所以在磁盤讀寫時可以進行其他一些操作以充分利用CPU。關于事件信號相關內容請參考4.4節(jié) 進程/線程間同步中事件部分內容。并且在文件讀寫時提供OVERLAPPED數據。

      結構定義如下:
      typedef struct _OVERLAPPED { 
          ULONG_PTR  Internal; //系統(tǒng)使用
          ULONG_PTR  InternalHigh; //系統(tǒng)使用
          DWORD  Offset; // 文件讀或寫的開始位置低32位,對于命名管道和其他通信設備必須設置為0
          DWORD  OffsetHigh; // 文件讀或寫的開始位置高32位,對于命名管道和其他通信設備必須設置為0
          HANDLE hEvent; // 事件量,當操作完成時,這個時間會變?yōu)橛行盘枲顟B(tài)
      } OVERLAPPED; 
      
      //下面的代碼演示了文件異步讀取
      //并且比較了同步和異步之間的性能差異
      void DoDataDeal(BYTE *pbData,int iLen)
      {//對字節(jié)進行操作
      	Sleep(3*1000);//假設每次計算需要2秒鐘
      }
      //下面是使用異步讀的示范代碼,假設c:\temp\large_file.dat文件有130MB大?。ǎ?
      //每次讀入10MB字節(jié),并且對文件中每個字節(jié)進行操作,由于可以使用異步操作所以可以在下一次讀入數據的同時進行計算
      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字節(jié)
      		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字節(jié)
      		BYTE *pbBuf = new BYTE[1024*1024*10];//10MB字節(jié)
      		
      		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來進一步確認是否發(fā)生錯誤,例如下面的錯誤檢測代碼:
      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函數來得到異步函數的執(zhí)行情況
      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時使用同一個數據區(qū)。最后一個參數bWait表明是否等待異步操作結束時才返回,如果設置為TRUE就可以等待文件讀寫完成時返回,否則就會馬上返回,利用這個特點可以利用它來等待異步文件操作的結束(就如同等待事件變?yōu)橛行盘枲顟B(tài)一樣起到相同的作用)。

      在上面的例子中沒有過多的進行錯誤檢查,如果大家有興趣可以自己運行一下這個例子,看看異步文件讀寫對性能的影響。在我自己的計算機PII 450 IBM 30GB硬盤上同步讀比異步讀慢了大約10%左右,這主要時因為數據處理時間我設置為兩秒鐘,如果設置得足夠長,會顯示出異步和同步處理時的差異極限。此外由于磁盤緩存得作用也會影響結果,所以如果讀入的數據量更大將會產生更明顯的差異,這是因為雖然異步讀寫會在調用等待函數上也會耗費一些時間,所以如果數據量小就無法分辨出差異。

      請記住OVERLAPPED參數在文件操作執(zhí)行完以前不要手工修改結構內的值,因為系統(tǒng)會使用結構中的數據。

      對于WriteFile操作也可以用相同方法,在WriteFile中磁盤操作將耗費更多的時間,所以使用異步寫更能體現優(yōu)勢,你可以將這個例子改為磁盤寫后自己測試一下。

      下載利用ReadFile進行異步文件讀的示范代碼

      第二種方法,利用ReadFileEx/WriteFileEx,這對函數使用回調函數來進行讀寫完成的通知。

      BOOL ReadFileEx(
        HANDLE hFile,                                       // handle to file
        LPVOID lpBuffer,                                    // data buffer
        DWORD nNumberOfBytesToRead,                         // number of bytes to read
        LPOVERLAPPED lpOverlapped,                          // offset
        LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // completion routine
      );
      
      • hFile為文件句柄。
      • lpBuffer指明了寫入數據的內存區(qū)指針。
      • nNumberOfBytesToRead為要求讀入的數據字節(jié)數。
      • lpOverlapped為一個OVERLAPPED的結構,這個結構hEvent字段將被系統(tǒng)忽略,但是通過Offset和OffsetHigh字段來表明開始讀文件的位置。
      • lpCompletionRoutine為一個通知用的回調函數。
      函數的最后一個參數指明了一個回調函數,這個回調函數稱為一個告警函數。函數必須具有這樣的原型:
      VOID CALLBACK FileIOCompletionRoutine(
        DWORD dwErrorCode,                // completion code
        DWORD dwNumberOfBytesTransfered,  // number of bytes transferred
        LPOVERLAPPED lpOverlapped         // I/O information buffer
      );
      • dwErrorCode為錯誤代碼,如果為0表示正確,為ERROR_HANDLE_EOF表示到達文件的末尾。
      • dwNumberOfBytesTransfered為成功傳送的字節(jié)數,如果發(fā)生錯誤,這個值為0。
      • lpOverlapped為一個OVERLAPPED的結構,這個結構必須與調用ReadFileEx時指向相同的數據區(qū),并且在調用ReadFileEx后不能手工更改這個結構中的字段。

      那么如何檢測回調函數已經被調用了(文件操作已經完成),你可以設置一個全局的同步量來進行通知,但是系統(tǒng)提供了其他簡便的方法供開發(fā)者使用,這就是SleepEx WaitForMultipleObjectsEx 和WaitForSingleObjectEx。

      當線程調用ReadFileEx或WriteFileEx時,提供了一個告警函數,這個告警函數會被放入一個隊列,當操作完成時操作系統(tǒng)會從隊列中取出這些函數進行調用。所以對于屬于本進程內部所產生的需要等待被調用的告警函數來說可以直接使用SleepEx對當前線程進行休眠并等待當前提交所有的告警函數被執(zhí)行。如果希望等待指定文件操作上的告警函數被調用你可以使用WaitForMultipleObjectsEx或WaitForSingleObjectEx。這三個函數的原型為:

      DWORD SleepEx(
        DWORD dwMilliseconds,  // time-out interval
        BOOL bAlertable        // early completion option
      );
      DWORD WaitForSingleObjectEx(
        HANDLE hHandle,        // handle to object
        DWORD dwMilliseconds,  // time-out interval
        BOOL bAlertable        // alertable option
      );
      DWORD WaitForMultipleObjectsEx(
        DWORD nCount,             // number of handles in array
        CONST HANDLE *lpHandles,  // object-handle array
        BOOL fWaitAll,            // wait option
        DWORD dwMilliseconds,     // time-out interval
        BOOL bAlertable           // alertable option
      );
      
      這三個函數和Sleep WaitForSingleObject WaitForMultipleObjects的差別就在于多了最后一個參數bAlertable,這個參數需要設置為TRUE表明等待文件異步操作的完成。通過檢測函數返回值可以得知文件操作是否完成,例如下面的代碼:
      	ReadFileEx(hFile,pbRead,1024*1024*50,&overlap,MyIORoutine);
      	while(WAIT_IO_COMPLETION != SleepEx(1,TRUE) )//檢測文件操作是否完成
      	//while (WaitForSingleObjectEx(hFile,1,TRUE) != WAIT_OBJECT_0 ) 
      	//在這里WaitForSingleObjectEx和SleepEx具有相同作用
      	{
      		DoSomething();
      	}
      
      對于SleepEx來說如果返回WAIT_IO_COMPLETION則表示異步操作完成,而對于文件對象來說如果異步操作完成文件對象就會變?yōu)橛行盘枲顟B(tài)。下面的例子是一個利用告警回調函數實現的文件異步讀寫。
      VOID CALLBACK MyIORoutine(
        DWORD dwErrorCode,                // completion code
        DWORD dwNumberOfBytesTransfered,  // number of bytes transferred
        LPOVERLAPPED lpOverlapped         // I/O information buffer
      )
      {//定義一個簡單的回調函數
      	printf("文件讀完成\n");
      }
      
      void DoSomething(void)
      {
      	printf("current time %d\n",GetTickCount());
      	Sleep(2000);//假設耗時的操作需要兩秒鐘
      }
      
      //下面是使用異步讀的示范代碼,假設c:\temp\large_file.dat文件有130MB大?。ǎ?
      //一次性讀入50MB字節(jié),在讀入的過程中進行一些其他操作
      void ReadM(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 )
      	{
      		BYTE *pbRead = new BYTE[1024*1024*50];//50MB字節(jié)
      		OVERLAPPED overlap;
      		overlap.Offset = 0;
      		overlap.OffsetHigh =0;
      		overlap.hEvent = NULL; //使用告警函數時無需要使用事件
      		
      		DWORD dwBegin= GetTickCount();//記錄開始時間
      		printf("begin time %d\n",dwBegin);
      		ReadFileEx(hFile,pbRead,1024*1024*50,&overlap,MyIORoutine);
      		while(WAIT_IO_COMPLETION != SleepEx(1,TRUE) )//檢測文件操作是否完成
      		//while (WaitForSingleObjectEx(hFile,1,TRUE) != WAIT_OBJECT_0 ) 
      		//在這里WaitForSingleObjectEx和SleepEx具有相同作用
      		{
      			DoSomething();//在文件讀的執(zhí)行過程中進行其他操作
      		}
      		printf("耗時 %d\n",GetTickCount()-dwBegin);
      		//操作完成
      		CloseHandle(hFile);
      		delete pbRead;
      	}
      }
      

      WriteFileEx的用法與ReadFileEx的用法是類似的。

      下載利用ReadFileEx進行異步文件讀的示范代碼

      在磁盤操作中磁盤寫比讀需要花更多的時間,并且大文件的異步寫可以更加有效的提高CPU利用率。但是異步操作會給開發(fā)和調試帶來一些麻煩,所以我建議除非在非常必要(性能要求非常高,文件非常大)的情況下才使用異步的磁盤讀寫。再提一點,對于磁盤文件的異步操作的方式同樣可以用于上章所講的命名管道,或者是串口的異步操作。

      文件加鎖時在打開文件后對文件的某個區(qū)域加鎖,加鎖后可以防止其他進程對該區(qū)域數據進行讀取。相關的函數為:

      BOOL LockFile(
        HANDLE hFile,                   // 文件句柄
        DWORD dwFileOffsetLow,          // 文件加鎖開始位置低32位
        DWORD dwFileOffsetHigh,         // 文件加鎖開始位置高32位
        DWORD nNumberOfBytesToLockLow,  // 區(qū)域長度低32位
        DWORD nNumberOfBytesToLockHigh  // 區(qū)域長度高32位
      );
      BOOL UnlockFile(
        HANDLE hFile,                    // 文件句柄
        DWORD dwFileOffsetLow,          // 文件解鎖開始位置低32位
        DWORD dwFileOffsetHigh,         // 文件解鎖開始位置高32位
        DWORD nNumberOfBytesToLockLow,  // 區(qū)域長度低32位
        DWORD nNumberOfBytesToLockHigh  // 區(qū)域長度高32位
      );
      
      在文件加鎖和解鎖上需要有對應關系,這種對應關系就是對A區(qū)域加鎖后必須對A區(qū)域解鎖后才可以對其他區(qū)域解鎖,而且必須是一對一的關系,也就是說調用一次對A區(qū)域的加鎖函數就必須調用一次對A區(qū)域的解鎖函數,而不能對一個區(qū)域加鎖后分次對該區(qū)域的不同部分解鎖。

      在MFC中對文件操作進行了封裝,CFile中封裝了各種文件操作。在CFile中常用的成員函數有以下這些:

      CFile( LPCTSTR lpszFileName, UINT nOpenFlags ); //打開文件
      
      virtual BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL ); //打開文件
      uOpenFlags為打開文件時的參數,可以取的以下值的組合:
      CFile::modeRead / CFile::modeReadWrite / CFile::modeWrite 讀寫模式
      CFile::modeCreate 創(chuàng)建文件
      CFile::shareDenyNone / CFile::shareDenyRead / CFile::shareDenyWrite 共享設置
      CFile::typeText / CFile::typeBinary 以文本形式還時二進制形式打開文件
      
      virtual void Close( ); //關閉文件
      
      virtual UINT Read( void* lpBuf, UINT nCount ); //讀文件
      
      virtual void Write( const void* lpBuf, UINT nCount ); // 寫文件
      
      virtual LONG Seek( LONG lOff, UINT nFrom ); //設置文件指針
      
      void SeekToBegin( );//移動文件指針到文件頭
      
      DWORD SeekToEnd( );//移動文件指針到文件尾
      
      virtual void LockRange( DWORD dwPos, DWORD dwCount ); //鎖定文件
      
      virtual void UnlockRange( DWORD dwPos, DWORD dwCount ); //解鎖文件
      

      CStdioFile是CFile的派生類,主要是完成對文本文件的操作,它只有兩個成員函數:

      BOOL ReadString(CString& rString); //讀入文件中一行
      
      void WriteString( LPCTSTR lpsz );//將字符串作為一行寫入文件
      

        本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多