同步和非同步 I/O

另請參閱 I/O 相關範例應用程式

有兩種類型的輸入/輸出 (I/O) 同步處理:同步 I/O 和非同步 I/O。 非同步 I/O 也稱為重迭 I/O。

同步檔案 I/O中,執行緒會啟動 I/O 作業,並立即進入等候狀態,直到 I/O 要求完成為止。 執行 非同步檔案 I/O 的執行緒會呼叫適當的函式,將 I/O 要求傳送至核心。 如果核心接受要求,呼叫端執行緒會繼續處理另一個作業,直到核心向執行緒發出 I/O 作業完成的訊號為止。 然後它會中斷其目前的作業,並視需要處理來自 I/O 作業的資料。

下圖說明這兩種同步處理類型。

同步和非同步 i/o

在預期 I/O 要求需要大量時間的情況下,例如大型資料庫的重新整理或備份或慢速通訊連結,非同步 I/O 通常是優化處理效率的好方法。 不過,對於相對快速的 I/O 作業,處理核心 I/O 要求和核心訊號的額外負荷可能會讓非同步 I/O 變得較不有用,特別是需要進行許多快速 I/O 作業時。 在此情況下,同步 I/O 會比較好。 如何完成這些工作的機制和實作詳細資料會根據所使用的裝置控制碼類型以及應用程式的特定需求而有所不同。 換句話說,通常有多種方式可以解決問題。

同步和非同步 I/O 考慮

如果針對同步 I/O (開啟檔案或裝置,也就是說,FILE_FLAG_OVERLAPPED未指定 ) 則 WriteFile 等函式的後續呼叫可能會封鎖呼叫執行緒的執行,直到發生下列其中一個事件為止:

  • I/O 作業在此範例中完成 (,即資料寫入) 。
  • 發生 I/O 錯誤。 (例如,管道會從另一端關閉。)
  • 例如,呼叫本身發生錯誤 (一或多個參數無效) 。
  • 進程中的另一個執行緒會使用封鎖執行緒的執行緒控制碼呼叫 CancelSynchronousIo 函式,這會終止該執行緒的 I/O,而 I/O 作業失敗。
  • 封鎖的執行緒會由系統終止;例如,進程本身已終止,或者另一個執行緒會使用封鎖執行緒的控制碼呼叫 TerminateThread 函式。 (這通常被視為最後一個方式,而且不是良好的應用程式設計。)

在某些情況下,應用程式的設計和用途可能會無法接受此延遲,因此應用程式設計工具應該考慮使用非同步 I/O 搭配適當的執行緒同步處理物件,例如 I/O 完成埠。 如需執行緒同步處理的詳細資訊,請參閱 關於同步處理

進程會藉由在dwFlagsAndAttributes參數中指定FILE_FLAG_OVERLAPPED旗標,在CreateFile的呼叫中開啟非同步 I/O 的檔案。 如果未指定 FILE_FLAG_OVERLAPPED ,則會針對同步 I/O 開啟檔案。 當檔案已針對非同步 I/O 開啟時, 迭結構的指標會傳遞至 ReadFileWriteFile的呼叫。 執行同步 I/O 時,呼叫 ReadFileWriteFile時不需要此結構。

注意

如果檔案或裝置已針對非同步 I/O 開啟,則使用該控制碼的 WriteFile 等函式的後續呼叫通常會立即傳回,但也可以在封鎖執行時以同步方式運作。 如需詳細資訊,請參閱https://support.microsoft.com/kb/156932

 

雖然CreateFile是用來開啟檔案、磁片區、匿名管道和其他類似裝置的最常見函式,但 I/O 作業也可以使用來自通訊端接受函式所建立之通訊端等其他系統物件的控制碼類型廣播來執行。

使用FILE_FLAG_BACKUP_SEMANTICS屬性呼叫CreateFile函式,以取得目錄物件的控制碼。 目錄控制碼幾乎永遠不會使用—備份應用程式是通常會使用這些控制碼的其中一個應用程式。

開啟非同步 I/O 的檔案物件之後,必須正確建立、初始化重 結構,並傳入每個呼叫函式,例如 ReadFileWriteFile。 在非同步讀取和寫入作業中使用 OVERLAPPED 結構時,請記住下列事項:

  • 在檔案物件的所有非同步 I/O 作業完成之前,請勿解除配置或修改 OVERLAPPED 結構或資料緩衝區。
  • 如果您將 重迭 結構的指標宣告為區域變數,在檔案物件的所有非同步 I/O 作業都完成之前,請勿結束本機函式。 如果本機函式提前結束, 則 OVERLAPPED 結構將會超出範圍,而且無法存取它遇到該函式外部的任何 ReadFileWriteFile 函式。

您也可以建立事件,並將控制碼放在 OVERLAPPED 結構中;等候 式接著可用來等候 I/O 作業完成,方法是等候事件控制碼。

如先前所述,使用非同步控制碼時,應用程式在判斷何時釋放與該控制碼上指定 I/O 作業相關聯的資源時,應該小心。 如果控制碼提前解除配置, ReadFileWriteFile 可能會錯誤地回報 I/O 作業已完成。 此外,WriteFile函式有時會傳回TRUE,且GetLastError值為ERROR_SUCCESS,即使它是使用非同步控制碼 (,也可以使用ERROR_IO_PENDING) 傳回FALSE。 程式設計人員習慣同步 I/O 設計,通常此時會釋放資料緩衝區資源,因為 TRUEERROR_SUCCESS 表示作業已完成。 不過,如果 I/O 完成埠 與這個非同步控制碼搭配使用,即使 I/O 作業立即完成,也會傳送完成封包。 換句話說,如果應用程式在 WriteFile 傳回 TRUE 之後釋放資源,除了 I/O 完成埠常式中的 ERROR_SUCCESS 之外,它也會有雙可用錯誤狀況。 在此範例中,建議允許完成埠常式完全負責這類資源的所有釋放作業。

系統不會在非同步控制碼上維護檔案指標,以支援檔案指標的檔案和裝置 (,也就是搜尋裝置) ,因此必須將檔案位置傳遞至 重迭 結構相關位移資料成員中的讀取和寫入函式。 如需詳細資訊,請參閱 WriteFileReadFile

同步控制碼的檔案指標位置是由系統維護,因為資料是讀取或寫入的,也可以使用 SetFilePointerSetFilePointerEx 函式來更新。

應用程式也可以等候檔案控制代碼同步處理 I/O 作業完成,但這麼做需要特別小心。 每次啟動 I/O 作業時,作業系統都會將檔案控制碼設定為非簽署狀態。 每次完成 I/O 作業時,作業系統都會將檔案控制碼設定為已發出訊號的狀態。 因此,如果應用程式啟動兩個 I/O 作業,並在檔案控制代碼上等候,則無法判斷當控制碼設定為訊號狀態時完成的作業。 如果應用程式必須在單一檔案上執行多個非同步 I/O 作業,它應該在每個 I/O 作業的特定 OVERLAPPED 結構中等候事件控制碼,而不是在一般檔案控制碼上。

若要取消所有擱置的非同步 I/O 作業,請使用:

  • CancelIo — 此函式只會取消所指定檔案控制碼的呼叫執行緒所發出的作業。
  • CancelIoEx— 此函式會取消執行緒針對指定的檔案控制代碼發出的所有作業。

使用 CancelSynchronousIo 取消暫止的同步 I/O 作業。

ReadFileExWriteFileEx函式可讓應用程式指定常式來執行 (請參閱非同步 I/O 要求完成時的FileIOCompletionRoutine) 。