ユーザー モードのスケジュール設定
警告
Windows 11時点では、ユーザー モードのスケジュール設定はサポートされていません。 すべての呼び出しがエラー ERROR_NOT_SUPPORTED
で失敗します。
ユーザー モード スケジューリング (UMS) は、アプリケーションが独自のスレッドをスケジュールするために使用できる軽量のメカニズムです。 アプリケーションは、 システム スケジューラ を使用せずにユーザー モードで UMS スレッドを切り替え、UMS スレッドがカーネルでブロックされた場合にプロセッサの制御を回復できます。 UMS スレッドは、1 つのスレッドのスレッド コンテキストを共有する代わりに、各 UMS スレッドに独自のスレッド コンテキストがある点で ファイバー とは異なります。 ユーザー モードでスレッドを切り替える機能により、少数のシステム呼び出しを必要とする多数の短時間の作業項目を管理するための スレッド プール よりも UMS の効率が高くなります。
UMS は、マルチプロセッサまたはマルチコア システムで多数のスレッドを同時に効率的に実行する必要がある高パフォーマンス要件のアプリケーションに推奨されます。 UMS を利用するには、アプリケーションでアプリケーションの UMS スレッドを管理し、いつ実行するかを決定するスケジューラ コンポーネントを実装する必要があります。 開発者は、アプリケーションのパフォーマンス要件が、このようなコンポーネントの開発に関連する作業を正当化するかどうかを検討する必要があります。 中程度のパフォーマンス要件を持つアプリケーションは、システム スケジューラがスレッドをスケジュールできるようにすることで、より適切に提供される場合があります。
UMS は、AMD64 および Itanium バージョンの Windows 7 および Windows Server 2008 R2 から Windows 10 バージョン 21H2 および Windows Server 2022 で実行されている 64 ビット アプリケーションで使用できます。 この機能は、Arm64、32 ビット バージョンの Windows、またはWindows 11では使用できません。
詳細については、次のセクションを参照してください。
- UMS スケジューラ
- UMS スケジューラ スレッド
- UMS ワーカー スレッド、スレッド コンテキスト、および完了リスト
- UMS スケジューラ エントリ ポイント関数
- UMS スレッドの実行
- UMS のベスト プラクティス
UMS スケジューラ
アプリケーションの UMS スケジューラは、UMS スレッドの作成、管理、削除、および実行する UMS スレッドの決定を担当します。 アプリケーションのスケジューラは、次のタスクを実行します。
- アプリケーションが UMS ワーカー スレッドを実行するプロセッサごとに 1 つの UMS スケジューラ スレッドを作成します。
- アプリケーションの作業を実行する UMS ワーカー スレッドを作成します。
- 実行する準備ができているワーカー スレッドの独自の準備完了スレッド キューを維持し、アプリケーションのスケジュール ポリシーに基づいて実行するスレッドを選択します。
- カーネルでの処理が完了した後、システムがスレッドをキューに入れる 1 つ以上の完了リストを作成および監視します。 これには、新しく作成されたワーカー スレッドと、ブロック解除されるシステム呼び出しで以前にブロックされたスレッドが含まれます。
- システムからの通知を処理するスケジューラ エントリ ポイント関数を提供します。 システムは、スケジューラ スレッドの作成時、システム呼び出しでワーカー スレッド ブロック、またはワーカー スレッドが明示的に制御を生成するときに、エントリ ポイント関数を呼び出します。
- 実行が完了したワーカー スレッドのクリーンアップ タスクを実行します。
- アプリケーションによって要求されたときに、スケジューラの順番にシャットダウンを実行します。
UMS スケジューラ スレッド
UMS スケジューラ スレッドは、 EnterUmsSchedulingMode 関数を呼び出して UMS に変換した通常のスレッドです。 システム スケジューラは、UMS スケジューラ スレッドが他の準備完了スレッドとの相対的な優先度に基づいて実行されるタイミングを決定します。 スケジューラ スレッドが実行されるプロセッサは、UMS 以外のスレッドの場合と同じように、スレッドのアフィニティの影響を受けます。
EnterUmsSchedulingMode の呼び出し元は、UMS スケジューラ スレッドに関連付ける完了リストと UmsSchedulerProc エントリ ポイント関数を指定します。 呼び出し元スレッドの UMS への変換が完了すると、システムは指定されたエントリ ポイント関数を呼び出します。 スケジューラ エントリ ポイント関数は、指定されたスレッドの適切な次のアクションを決定する役割を担います。 詳細については、このトピックで後述する 「UMS スケジューラ エントリ ポイント関数 」を参照してください。
アプリケーションでは、UMS スレッドの実行に使用されるプロセッサごとに 1 つの UMS スケジューラ スレッドを作成できます。 また、アプリケーションは特定の論理プロセッサに対して各 UMS スケジューラ スレッドのアフィニティを設定する場合もあります。この場合、関連のないスレッドがそのプロセッサで実行されるのを除外し、そのスケジューラ スレッドに対して実質的に予約する傾向があります。 この方法でスレッド アフィニティを設定すると、システムで実行されている可能性のある他のプロセスが不足することで、システム全体のパフォーマンスに影響を与える可能性があることに注意してください。 スレッド アフィニティの詳細については、「 複数のプロセッサ」を参照してください。
UMS ワーカー スレッド、スレッド コンテキスト、および完了リスト
UMS ワーカー スレッドは、PROC_THREAD_ATTRIBUTE_UMS_THREAD属性で CreateRemoteThreadEx を呼び出し、UMS スレッド コンテキストと完了リストを指定することによって作成されます。
UMS スレッド コンテキストは、ワーカー スレッドの UMS スレッドの状態を表し、UMS 関数呼び出しでワーカー スレッドを識別するために使用されます。 CreateUmsThreadContext を呼び出すことによって作成されます。
完了リストは、 CreateUmsCompletionList 関数を呼び出すことによって作成されます。 完了リストは、カーネルで実行が完了し、ユーザー モードで実行できる UMS ワーカー スレッドを受け取ります。 ワーカー スレッドを完了リストにキューに入れるのはシステムだけです。 新しい UMS ワーカー スレッドは、スレッドの作成時に指定された完了リストに自動的にキューに入れられます。 以前にブロックされたワーカー スレッドは、ブロックされなくなったら、完了リストにもキューに入れられます。
各 UMS スケジューラ スレッドは、1 つの完了リストに関連付けられます。 ただし、同じ完了リストを任意の数の UMS スケジューラ スレッドに関連付けることができ、スケジューラ スレッドは、ポインターを持つ任意の完了リストから UMS コンテキストを取得できます。
各完了リストには、1 つ以上のワーカー スレッドを空のリストにキューに入れると、システムによって通知されるイベントが関連付けられています。 GetUmsCompletionListEvent 関数は、指定された完了リストのイベントへのハンドルを取得します。 アプリケーションは、複数の完了リスト イベントと、そのアプリケーションにとって意味のある他のイベントを待機できます。
UMS スケジューラ エントリ ポイント関数
アプリケーションのスケジューラ エントリ ポイント関数は、 UmsSchedulerProc 関数として実装されます。 システムは、アプリケーションのスケジューラ エントリ ポイント関数を次の時刻に呼び出します。
- 非 UMS スレッドが 、EnterUmsSchedulingMode を呼び出して UMS スケジューラ スレッドに変換される場合。
- UMS ワーカー スレッドが UmsThreadYield を呼び出す場合。
- UMS ワーカー スレッドがシステム呼び出しやページ フォールトなどのシステム サービスでブロックする場合。
UmsSchedulerProc 関数の Reason パラメーターは、エントリ ポイント関数が呼び出された理由を指定します。 新しい UMS スケジューラ スレッドが作成されたためにエントリ ポイント関数が呼び出された場合、 SchedulerParam パラメーターには 、EnterUmsSchedulingMode の呼び出し元によって指定されたデータが含まれます。 UMS ワーカー スレッドが生成されたためにエントリ ポイント関数が呼び出された場合、 SchedulerParam パラメーターには UmsThreadYield の呼び出し元によって指定されたデータが含まれます。 カーネルで UMS ワーカー スレッドがブロックされたためにエントリ ポイント関数が呼び出された場合、 SchedulerParam パラメーターは NULL です。
スケジューラ エントリ ポイント関数は、指定されたスレッドの適切な次のアクションを決定する役割を担います。 たとえば、ワーカー スレッドがブロックされている場合、スケジューラ エントリ ポイント関数は、次に使用可能な準備ができている UMS ワーカー スレッドを実行する可能性があります。
スケジューラ エントリ ポイント関数が呼び出されると、アプリケーションのスケジューラは DequeueUmsCompletionListItems 関数を呼び出して、関連付けられている完了リスト内のすべての項目の取得を試みる必要があります。 この関数は、カーネルでの処理が完了し、ユーザー モードで実行する準備ができている UMS スレッド コンテキストの一覧を取得します。 アプリケーションのスケジューラは、アプリケーションで予期しない動作を引き起こす可能性があるため、この一覧から UMS スレッドを直接実行しないでください。 代わりに、スケジューラは、コンテキストごとに GetNextUmsListItem 関数を 1 回呼び出して、すべての UMS スレッド コンテキストを取得し、スケジューラの準備完了スレッド キューに UMS スレッド コンテキストを挿入し、準備完了スレッド キューから UMS スレッドのみを実行する必要があります。
スケジューラが複数のイベントを待機する必要がない場合は、0 以外のタイムアウト パラメーターを指定して DequeueUmsCompletionListItems を 呼び出して、関数が完了リスト イベントを待機してから戻るようにする必要があります。 スケジューラが複数の完了リスト イベントを待機する必要がある場合は、タイムアウト パラメーターが 0 の DequeueUmsCompletionListItems を 呼び出して、完了リストが空であっても、関数が直ちに戻るようにする必要があります。 この場合、スケジューラは、 たとえば WaitForMultipleObjects を使用して、完了リスト イベントで明示的に待機できます。
UMS スレッドの実行
新しく作成された UMS ワーカー スレッドは、指定した完了リストにキューに登録され、アプリケーションの UMS スケジューラによって実行が選択されるまで実行は開始されません。 これは、UMS 以外のスレッドとは異なり、呼び出し元がスレッドを明示的に作成しない限り、システム スケジューラによって自動的に実行がスケジュールされます。
スケジューラは、ワーカー スレッドの UMS コンテキストで ExecuteUmsThread を呼び出してワーカー スレッドを実行します。 UMS ワーカー スレッドは、 UmsThreadYield 関数、ブロック、または終了を呼び出して生成されるまで実行されます。
UMS のベスト プラクティス
UMS を実装するアプリケーションは、次のベスト プラクティスに従う必要があります。
- UMS スレッド コンテキストの基になる構造体はシステムによって管理されるため、直接変更しないでください。 代わりに、 QueryUmsThreadInformation と SetUmsThreadInformation を使用して、UMS ワーカー スレッドに関する情報を取得および設定します。
- デッドロックを防ぐために、UMS スケジューラ スレッドは UMS ワーカー スレッドとロックを共有しないようにする必要があります。 これには、ヒープからの割り当てや DLL の読み込みなどの操作によって間接的に取得される、アプリケーションによって作成されたロックとシステム ロックの両方が含まれます。 たとえば、スケジューラが DLL を読み込む UMS ワーカー スレッドを実行するとします。 ワーカー スレッドはローダー ロックとブロックを取得します。 システムはスケジューラ エントリ ポイント関数を呼び出し、DLL を読み込みます。 ローダー ロックは既に保持されており、最初のスレッドのブロックが解除されるまで解放できないため、デッドロックが発生します。 この問題を回避するには、UMS ワーカー スレッドとロックを共有する可能性がある作業を専用の UMS ワーカー スレッドまたは UMS 以外のスレッドに委任します。
- ほとんどの処理がユーザー モードで行われる場合、UMS は最も効率的です。 可能な限り、UMS ワーカー スレッドでシステム呼び出しを行わないようにします。
- UMS ワーカー スレッドでは、システム スケジューラが使用されていると想定しないでください。 この仮定は微妙な影響を及ぼす可能性があります。たとえば、不明なコード内のスレッドでスレッドの優先度またはアフィニティが設定されている場合、UMS スケジューラによって引き続きオーバーライドされる可能性があります。 システム スケジューラが使用されていることを前提とするコードは、想定どおりに動作しない可能性があり、UMS スレッドによって呼び出されると中断する可能性があります。
- システムでは、UMS ワーカー スレッドのスレッド コンテキストをロックする必要がある場合があります。 たとえば、カーネル モードの非同期プロシージャ 呼び出し (APC) は UMS スレッドのコンテキストを変更する可能性があるため、スレッド コンテキストをロックする必要があります。 スケジューラがロックされている間に UMS スレッド コンテキストを実行しようとすると、呼び出しは失敗します。 この動作は仕様であり、スケジューラは UMS スレッド コンテキストへのアクセスを再試行するように設計する必要があります。