マネージド スレッド プール

System.Threading.ThreadPool クラスを使用すると、システムによって管理されるワーカー スレッドのプールがアプリケーションに提供されます。これを利用すると、開発者は、スレッド管理ではなくアプリケーション タスクに注意を集中できるようになります。 バックグラウンド処理が必要な短いタスクがある場合、マネージド スレッド プールを使用すると、複数のスレッドを簡単に利用できます。 Framework 4 以降ではスレッド プールのスレッドで非同期タスクを実行する Task オブジェクトと Task<TResult> オブジェクトを作成できるため、スレッド プールが飛躍的に使いやすくなっています。

.NET では、タスク並列ライブラリ (TPL)の操作、非同期 I/O 完了、タイマー コールバック、登録済みの待機操作、デリゲートを使用した非同期メソッド呼び出し、System.Net ソケット接続などのさまざまな目的でスレッド プールのスレッドを使用します。

スレッド プールの特徴

スレッド プールのスレッドはバックグラウンド スレッドです。 各スレッドは、既定のスタック サイズを使用し、既定の優先順位で実行されます。また、各スレッドはマルチスレッド アパートメント内にあります。 タスクを完了したスレッド プール内のスレッドは待機スレッドのキューに戻ります。 再利用はこの時点から可能です。 これにより、タスクごとに新しいスレッドを作成するという負荷がアプリケーションにかからなくなります。

プロセスごとにスレッド プールが 1 つだけあります。

スレッド プールのスレッドでの例外

スレッド プールのスレッドで未処理の例外が発生すると、プロセスが終了します。 このルールには次の 3 つの例外があります。

  • Thread.Abort が呼び出されたため、スレッド プールのスレッドに System.Threading.ThreadAbortException がスローされる。
  • アプリケーション ドメインがアンロードされるため、スレッド プールのスレッドに System.AppDomainUnloadedException がスローされる。
  • 共通言語ランタイムまたはホスト プロセスがスレッドを終了する。

詳しくは、「マネージド スレッドの例外」をご覧ください。

スレッド プールのスレッドの最大数

スレッド プールのキューに登録できる操作の数は、使用可能なメモリによってのみ制限されます。 ただし、スレッド プールによって、プロセスで同時にアクティブにできるスレッドの数が制限されます。 すべてのスレッド プールのスレッドがビジー状態の場合、追加の作業項目は、それらを実行するスレッドが空くまでキューに登録されます。 プロセスのスレッド プールの既定のサイズは、仮想アドレス空間のサイズなど、いくつかの要素によって決まります。 スレッドの数は、プロセスで ThreadPool.GetMaxThreads メソッドを呼び出せば確認できます。

スレッドの最大数を制御するには、ThreadPool.GetMaxThreads メソッドと ThreadPool.SetMaxThreads メソッドを使用します。

注意

共通言語ランタイムをホストするコードでは、ICorThreadpool::CorSetMaxThreads メソッドを使用してサイズを設定できます。

スレッド プールの最小値

スレッド プールでは、カテゴリごとに指定された最小値に達するまで、要求に応じて新しいワーカー スレッドまたは I/O 完了スレッドが提供されます。 これらの最小値は、ThreadPool.GetMinThreads メソッドを使用して取得できます。

注意

要求が少ないときは、スレッド プールの実際のスレッド数が最小値を下回る場合があります。

スレッド プールの最小値に達すると、追加のスレッドが作成されるか、いくつかのタスクが完了するまで待機状態になります。 スループットを最適化するために、スレッド プールでワーカー スレッドの作成と破棄が行われます。スループットは、タスクの単位時間あたりの完了数として定義されます。 スレッドが少なすぎると使用可能なリソースが最適に使用されない可能性があり、スレッドが多すぎるとリソースの競合が増える可能性があります。

注意事項

アイドル スレッドの最小数は、ThreadPool.SetMinThreads メソッドを使用して増やすことができます。 ただし、これらの値を必要以上に大きくすると、パフォーマンスの問題が発生する可能性があります。 同時に開始するタスクの数が多すぎる場合は、すべてのタスクで処理速度が低下する可能性があります。 ほとんどの場合、スレッドを割り当てるためのスレッド プール独自のアルゴリズムを使用することでスレッド プールのパフォーマンスが向上します。

スレッド プールの使用

スレッド プールを使用する場合は、タスク並列ライブラリ (TPL) を使用すると最も簡単です。 TaskTask<TResult> などの TPL の型では、既定でスレッド プールのスレッドを使用してタスクが実行されます。

また、マネージド コードから ThreadPool.QueueUserWorkItem (またはアンマネージド コードから ICorThreadpool::CorQueueUserWorkItem) を呼び出し、タスクを実行するメソッドを表す System.Threading.WaitCallback デリゲートを渡すことによってスレッド プールを使用することもできます。

スレッド プールを使用するもう 1 つの方法として、待機操作の関連ワーク アイテムをキューに置く方法もあります。これには、ThreadPool.RegisterWaitForSingleObject メソッドを使用し、System.Threading.WaitHandle を渡します。その WaitHandle がシグナル状態になるかタイムアウトになると、System.Threading.WaitOrTimerCallback デリゲートによって表されるメソッドが呼び出されます。 スレッド プールのスレッドを使用してコールバック メソッドが呼び出されます。

参照先の API ページを例としてご確認ください。

セキュリティ チェックのスキップ

スレッド プールでは、ThreadPool.UnsafeQueueUserWorkItem メソッドと ThreadPool.UnsafeRegisterWaitForSingleObject メソッドも使用できます。 これらのメソッドは、呼び出し元のスタックが、キューに配置されているタスクの実行時に行われるセキュリティ チェックと無関係であることが確認できる場合にのみ使用します。 ThreadPool.QueueUserWorkItemThreadPool.RegisterWaitForSingleObject はどちらも呼び出し元のスタックを取り込みます。このスタックは、スレッドがタスクの実行を開始するときにスレッド プールのスレッドのスタックにマージされます。 セキュリティ チェックが必要な場合は、そのスタック全体をチェックする必要があります。 チェックは安全性を提供しますが、パフォーマンスへの影響もあります。

スレッド プールのスレッドを使用しない場合

次のような場合は、スレッド プールのスレッドを使用する代わりに独自のスレッドを作成して管理する方が適切です。

  • フォア グラウンド スレッドが必要な場合。
  • スレッドに特定の優先順位を設定する必要がある場合。
  • スレッドを長時間にわたってブロックするタスクがある場合。 スレッド プールには最大数のスレッドが存在するため、多数のスレッド プール スレッドがブロックされると、タスクを開始できなくなることがあります。
  • スレッドをシングルスレッド アパートメント内に配置する必要がある場合。 ThreadPool スレッドはすべてマルチスレッド アパートメント内にあります。
  • 安定した ID をスレッドに関連付ける必要がある場合、またはスレッドを特定のタスク専用にする必要がある場合。

参照