同期と重複入力と出力

ファイル、名前付きパイプ、シリアル通信デバイスに対して、同期または非同期 (重複とも呼ばれます) I/O 操作を実行できます。 WriteFileReadFileDeviceIoControlWaitCommEventConnectNamedPipeTransactNamedPipe 関数は、同期的または非同期的に実行できます。 ReadFileEx 関数と WriteFileEx 関数は、非同期でのみ実行できます。

関数が同期的に実行されると、操作が完了するまで戻りません。 つまり、呼び出し元のスレッドの実行は、時間のかかる操作が完了するまで待機している間、無期限にブロックされる可能性があります。 重複する操作に対して呼び出された関数は、操作が完了していない場合でも、すぐに返すことができます。 これにより、呼び出し元のスレッドが他のタスクを自由に実行できる間、時間のかかる I/O 操作をバックグラウンドで実行できます。 たとえば、1 つのスレッドで、異なるハンドルに対して同時 I/O 操作を実行したり、同じハンドルに対して同時の読み取り操作と書き込み操作を実行したりできます。

その実行を重複する操作の完了と同期するために、呼び出し元のスレッドは 、GetOverlappedResult 関数、 GetOverlappedResultEx 関数、または いずれかの待機関数 を使用して、重複した操作がいつ完了したかを判断します。 HasOverlappedIoCompleted マクロを使用して、完了をポーリングすることもできます。

保留中のすべての非同期 I/O 操作を取り消すには、 CancelIoEx 関数を使用し、取り消す要求を指定する OVERLAPPED 構造体を指定します。 CancelIo 関数を使用して、指定されたファイル ハンドルに対して呼び出し元スレッドによって発行された保留中の非同期 I/O 操作を取り消します。

重複する操作には、 FILE_FLAG_OVERLAPPED フラグを使用して作成されたファイル、名前付きパイプ、または通信デバイスが必要です。 スレッドが関数 ( ReadFile 関数など) を呼び出して重複する操作を実行する場合、呼び出し元のスレッドは OVERLAPPED 構造体へのポインターを指定する必要があります。 (このポインターが NULL の場合、関数の戻り値は操作が完了したことを誤って示している可能性があります)。イベントを使用して I/O 操作の完了を通知しない限り、 OVERLAPPED 構造体のすべてのメンバーを 0 に初期化する必要があります。 イベントを使用する場合、OVERLAPPED 構造体の hEvent メンバーは、割り当てられたイベント オブジェクトへのハンドルを指定します。 システムは、操作が完了する前に I/O 関数の呼び出しが戻ったときに、イベント オブジェクトの状態を非署名に設定します。 操作が完了したときに、イベント オブジェクトの状態がシグナルに設定されます。 イベントは、同時に複数の未処理の I/O 操作がある場合にのみ必要です。 イベントが使用されていない場合、完了した各 I/O 操作によって、ファイル、名前付きパイプ、または通信デバイスが通知されます。

重複する操作を実行するために関数が呼び出されると、関数が戻る前に操作が完了する可能性があります。 この場合、結果は操作が同期的に実行されたかのように処理されます。 ただし、操作が完了しなかった場合、関数の戻り値は FALSE になり、 GetLastError 関数は ERROR_IO_PENDINGを返します。

スレッドは、次の 2 つの方法のいずれかで重複する操作を管理できます。

  • 重複した操作が完了するまで待機するには、 GetOverlappedResult または GetOverlappedResultEx 関数を使用します。 GetOverlappedResultEx が使用されている場合、呼び出し元のスレッドは、重複する操作のタイムアウトを指定するか、アラート可能な待機を実行できます。
  • いずれかの待機関数で OVERLAPPED 構造体の手動リセット イベント オブジェクトへのハンドルを指定し、 待機関数 が戻った後に GetOverlappedResult または GetOverlappedResultEx を呼び出します。 関数は、完了した重複した操作の結果を返し、そのような情報が適切な関数の場合は、転送された実際のバイト数を報告します。

1 つのスレッドで複数の同時重複操作を実行する場合、呼び出し元のスレッドは各操作に 対して OVERLAPPED 構造体を指定する必要があります。 各 OVERLAPPED 構造体では、異なる手動リセット イベント オブジェクトへのハンドルを指定する必要があります。 重複する操作のいずれかが完了するまで待機するために、スレッドは、複数オブジェクト待機 関数のいずれかで、すべての手動リセット イベント ハンドルを待機条件として指定します。 複数オブジェクト待機関数の戻り値は、通知された手動リセット イベント オブジェクトを示します。そのため、スレッドは、待機操作が完了した原因となった重複した操作を判断できます。

イベント オブジェクトを指定しないか、複数の操作で同じイベント オブジェクトを再利用するのではなく、重複する操作ごとに個別のイベント オブジェクトを使用する方が安全です。 OVERLAPPED 構造体にイベント オブジェクトが指定されていない場合、重複した操作が完了すると、システムはファイル、名前付きパイプ、または通信デバイスの状態を通知します。 したがって、待機関数ではこれらのハンドルを同期オブジェクトとして指定できますが、この目的での使用は管理が困難な場合があります。これは、同じファイル、名前付きパイプ、または通信デバイスで同時に重複する操作を実行するときに、オブジェクトの状態が通知される原因となった操作を知る方法がないためです。

スレッドは、そのスレッドの重複した操作によってのみイベントが通知されることを前提として、イベントを再利用しないでください。 イベントは、完了している重複した操作と同じスレッドで通知されます。 複数のスレッドで同じイベントを使用すると、そのイベントを使用して他のスレッドに対して最初と途中で操作が完了したスレッドに対してイベントが正しく通知される競合状態が発生する可能性があります。 次に、重なり合った次の操作が完了すると、そのイベントを使用しているすべてのスレッドに対してイベントが再度通知されます。また、重複するすべての操作が完了するまで、イベントが再度通知されます。

重複する操作、完了ルーチン、 および GetOverlappedResult 関数の使用例については、「 パイプの使用」を参照してください。

Windows Vista、Windows Server 2003、および Windows XP:

OVERLAPPED 構造体を再利用する場合は注意してください。 OVERLAPPED 構造体が複数のスレッドで再利用され、bWait パラメーターが TRUE に設定された GetOverlappedResult が呼び出された場合、呼び出し元のスレッドは、構造体を再利用する前に、関連付けられたイベントが通知されるようにする必要があります。 これは、GetOverlappedResult を呼び出した後に WaitForSingleObject 関数を使用して、操作が完了するまでスレッドを強制的に待機することで実現できます。 イベント オブジェクトは、手動リセット イベント オブジェクトである必要があることに注意してください。 autoreset イベント オブジェクトが使用されている場合、bWait パラメーターを TRUE に設定して GetOverlappedResult を呼び出すと、関数が無期限にブロックされます。 この動作は、アプリケーション マニフェストでサポートされているオペレーティング システムとして Windows 7 を指定するアプリケーションの Windows 7 および Windows Server 2008 R2 以降で変更されました。 詳細については、「 アプリケーション マニフェスト」を参照してください。

I/O の概念