I/O 完了ポート
I/O 完了ポートは、マルチプロセッサ システムで複数の非同期 I/O 要求を処理するための効率的なスレッドモデルを提供します。 プロセスが I/O 完了ポートを作成すると、システムは、これらの要求にサービスを提供する唯一の目的を持つスレッドの関連するキュー オブジェクトを作成します。 多くの同時非同期 I/O 要求を処理するプロセスは、I/O 要求を受信した時点でスレッドを作成するよりも、事前に割り当てられたスレッド プールと組み合わせて I/O 完了ポートを使用することで、より迅速かつ効率的に処理できます。
I/O 完了ポートのしくみ
CreateIoCompletionPort 関数は、I/O 完了ポートを作成し、そのポートに 1 つ以上のファイル ハンドルを関連付けます。 これらのファイル ハンドルのいずれかで非同期 I/O 操作が完了すると、I/O 完了パケットは、関連付けられている I/O 完了ポートに対して先入れ先出し (FIFO) 順にキューに入れられます。 このメカニズムの強力な用途の 1 つは、複数のファイル ハンドルの同期ポイントを 1 つのオブジェクトに結合することですが、他の便利なアプリケーションもあります。 パケットは FIFO 順にキューに入れられますが、別の順序でデキューされる可能性があることに注意してください。
手記
ここで使用するファイル ハンドル という用語は、ディスク上のファイルだけでなく、重複する I/O エンドポイントを表すシステム抽象化を指します。 たとえば、ネットワーク エンドポイント、TCP ソケット、名前付きパイプ、メール スロットなどです。 重複した I/O をサポートするシステム オブジェクトは、いずれも使用できます。 関連する I/O 関数の一覧については、このトピックの最後を参照してください。
ファイル ハンドルが完了ポートに関連付けられている場合、渡された状態ブロックは、パケットが完了ポートから削除されるまで更新されません。 唯一の例外は、元の操作がエラーで同期的に返される場合です。 スレッド (メイン スレッドまたはメイン スレッド自体によって作成されたもの) は、GetQueuedCompletionStatus 関数を使用して、非同期 I/O が完了するまで直接待機するのではなく、完了パケットが I/O 完了ポートにキューに入れるのを待機します。 I/O 完了ポートでの実行をブロックするスレッドは、先入れ先出し (LIFO) の順序で解放され、次の完了パケットはそのスレッドの I/O 完了ポートの FIFO キューからプルされます。 つまり、完了パケットがスレッドに解放されると、システムはそのポートに関連付けられている最後の (最新の) スレッドを解放し、最も古い I/O 完了の完了情報を渡します。
指定した I/O 完了ポートに対して任意の数のスレッド GetQueuedCompletionStatus を呼び出すことができますが、指定したスレッドが初めて GetQueuedCompletionStatus 呼び出すと、3 つのいずれかが発生するまで、指定した I/O 完了ポートに関連付けられます。スレッドが終了し、別の I/O 完了ポートが指定されます。 または I/O 完了ポートを閉じます。 つまり、1 つのスレッドを、最大で 1 つの I/O 完了ポートに関連付けることができます。
完了パケットが I/O 完了ポートにキューに登録されると、システムはまず、そのポートに関連付けられているスレッドの数を確認します。 実行中のスレッドの数がコンカレンシー値 (次のセクションで説明) より少ない場合、待機中のスレッド (最新のスレッド) の 1 つが完了パケットの処理を許可されます。 実行中のスレッドが処理を完了すると、通常は GetQueuedCompletionStatusを再度呼び出します。その時点で、次の完了パケットを返すか、キューが空の場合は待機します。
スレッドは、PostQueuedCompletionStatus 関数を使用して、I/O 完了ポートのキューに完了パケットを配置できます。 これにより、完了ポートを使用して、I/O システムからの I/O 完了パケットの受信に加えて、プロセスの他のスレッドからの通信を受信できます。 PostQueuedCompletionStatus 関数を使用すると、アプリケーションは、非同期 I/O 操作を開始せずに、独自の特殊な目的の完了パケットを I/O 完了ポートにキューに入れます。 これは、たとえば外部イベントをワーカー スレッドに通知する場合に便利です。
I/O 完了ポート ハンドルと、その特定の I/O 完了ポートに関連付けられているすべてのファイル ハンドルは、I/O 完了ポート への参照と呼ばれます。 I/O 完了ポートは、それ以上参照がない場合に解放されます。 そのため、I/O 完了ポートとそれに関連付けられているシステム リソースを解放するには、これらのハンドルをすべて適切に閉じる必要があります。 これらの条件が満たされたら、アプリケーションは、CloseHandle 関数を呼び出して、I/O 完了ポート ハンドルを閉じる必要があります。
手記
I/O 完了ポートは、I/O 完了ポートを作成したプロセスに関連付けられているため、プロセス間で共有することはできません。 ただし、同じプロセス内のスレッド間で 1 つのハンドルを共有できます。
スレッドとコンカレンシー
慎重に検討する I/O 完了ポートの最も重要なプロパティは、コンカレンシー値です。 完了ポートのコンカレンシー値は、NumberOfConcurrentThreads パラメーターを使用して CreateIoCompletionPort を使用して作成されるときに指定されます。 この値は、完了ポートに関連付けられている実行可能なスレッドの数を制限します。 完了ポートに関連付けられている実行可能なスレッドの合計数がコンカレンシー値に達すると、実行可能なスレッドの数がコンカレンシー値を下回るまで、システムはその完了ポートに関連付けられている後続のスレッドの実行をブロックします。
最も効率的なシナリオは、キューで待機している完了パケットがあるが、ポートがコンカレンシー制限に達したために待機を満たさない場合に発生します。 GetQueuedCompletionStatus 関数呼び出しで待機している 1 つ以上のスレッドのコンカレンシー値で何が起こるかを検討します。 この場合、キューに常に完了パケットが待機している場合、実行中のスレッドが GetQueuedCompletionStatus 呼び出すと、前述のようにスレッド キューが LIFO であるため、実行はブロックされません。 代わりに、このスレッドは、キューに登録された次の完了パケットをすぐに取得します。 実行中のスレッドが継続的に完了パケットを取得しており、他のスレッドを実行できないため、スレッド コンテキストの切り替えは発生しません。
手記
前の例では、余分なスレッドは役に立たず、実行されないように見えますが、実行中のスレッドが他のメカニズムによって待機状態にされたり、終了したり、関連付けられている I/O 完了ポートを閉じたりすることはありません。 アプリケーションを設計するときは、このようなスレッド実行の影響をすべて考慮してください。
コンカレンシー値に対して選択する最適な全体的な最大値は、コンピューター上の CPU の数です。 トランザクションで長い計算が必要な場合は、コンカレンシー値を大きくすると、より多くのスレッドを実行できるようになります。 各完了パケットの完了には時間がかかる場合がありますが、同時に処理される完了パケットが増えます。 コンカレンシー値をプロファイリング ツールと組み合わせて試して、アプリケーションに最適な効果を得ることができます。
また、同じ I/O 完了ポートに関連付けられている別の実行中のスレッドが他の理由 (SuspendThread 関数など) に入った場合に、GetQueuedCompletionStatus で待機しているスレッドが完了パケットを処理することもできます。 待機状態のスレッドが再び実行を開始すると、アクティブなスレッドの数がコンカレンシー値を超える短い期間が発生する可能性があります。 ただし、アクティブなスレッドの数がコンカレンシー値を下回るまで、システムは新しいアクティブ スレッドを許可しないことで、この数をすばやく減らします。 これは、アプリケーションがコンカレンシー値よりも多くのスレッドをスレッド プールに作成する理由の 1 つです。 スレッド プールの管理はこのトピックの範囲を超えていますが、経験則として、スレッド プール内のスレッド数は、システム上のプロセッサの 2 倍以上にすることをお勧めします。 スレッド プールの詳細については、「スレッド プールの」を参照してください。
サポートされている I/O 関数
次の関数を使用して、I/O 完了ポートを使用して完了する I/O 操作を開始できます。 I/O 完了ポート メカニズムを有効にするには、関数に OVERLAPPED 構造体のインスタンスと、以前に I/O 完了ポートに関連付けられたファイル ハンドル (CreateIoCompletionPort呼び出し) を渡す必要があります。
- AcceptExをする
- ConnectNamedPipeの
- DeviceIoControlの
- LockFileExの
- ReadDirectoryChangesW
- ReadFile
- TransactNamedPipeの
- WaitCommEventの
- WriteFileの
- WSASendMsgの
- WSASendToの
- WSASendをする
- WSARecvFromをする
- LPFN_WSARECVMSG (WSARecvMsg)
- WSARecvの
関連トピック
-
プロセスとスレッドの について
-
BindIoCompletionCallbackの
-
CreateIoCompletionPortの
-
GetQueuedCompletionStatusをする
-
GetQueuedCompletionStatusExの
-
PostQueuedCompletionStatusをする