Thread vs ThreadPool
-
04-07-2019 - |
質問
新しいスレッドを使用することと、スレッドプールのスレッドを使用することの違いは何ですか?どのようなパフォーマンス上の利点があり、明示的に作成したスレッドではなく、プールのスレッドの使用を検討する必要があるのはなぜですか?ここでは.NETについて具体的に考えていますが、一般的な例は問題ありません。
解決
スレッドプールは、頻繁かつ比較的短い操作に対して次のような利点を提供します
- 新しいスレッドを作成する代わりに、すでに作成されているスレッドを再利用する(高価なプロセス)
-
新しい作業項目の要求が急増した場合のスレッド作成率の調整(これは.NET 3.5のみにあると思います)
-
100個のスレッドプールタスクをキューに入れると、これらの要求を処理するために既に作成されているスレッド(たとえば10個)だけが使用されます。スレッドプールは頻繁にチェックを行い(3.5 SP1では500msごとと考えています)、キューに入れられたタスクがある場合は、1つの新しいスレッドを作成します。タスクが速い場合は、新しいスレッドの数が少なくなり、前もって100個のスレッドを作成するよりも、短いタスクで10個程度のスレッドを再利用する方が速くなります。
-
ワークロードに常に多数のスレッドプールリクエストが着信している場合、スレッドプールは、上記のプロセスによってプール内にスレッドを作成してワークロードに合わせて調整し、スレッドの数を増やします。リクエストの処理に利用可能
-
ここでは、スレッドプールが内部でどのように機能するかの詳細について
-
新しいスレッドを自分で作成することは、ジョブが比較的長時間実行される場合に適しています(おそらく1〜2秒程度ですが、特定の状況によって異なります)。
@Krzysztof-スレッドプールスレッドは、メインスレッドが終了すると停止するバックグラウンドスレッドです。手動で作成されたスレッドはデフォルトでフォアグラウンドになります(メインスレッドが終了した後も実行を続けます)が、それらのスレッドを開始する前にバックグラウンドに設定できます。
他のヒント
.NET管理スレッドプール:-
- 現在のワークロードと使用可能なハードウェアに基づいてサイズを変更します
- ワーカースレッドおよび完了ポートスレッド(特にIOのサービスに使用)が含まれています
- 多数の比較的短命の操作向けに最適化されています
他のスレッドプールの実装が存在します。これは、長時間実行される操作により適している可能性があります。
具体的には、スレッドプールを使用して、アプリが多すぎるスレッドを作成しないようにします。スレッドプールの最も重要な機能は作業キューです。つまり、マシンが十分にビジー状態になると、スレッドプールはすぐにさらにスレッドを生成するのではなく、リクエストをキューに入れます。
したがって、少数のスレッドを作成する場合は、自分で作成します。作成できるスレッドの数を事前に判断できない場合(たとえば、着信IOに応答して作成されるスレッド)、その作業が短命になる場合は、スレッドプールを使用します。その数がわからないが、その作業が長時間かかる場合は、プラットフォームに役立つものはありませんが、適切な代替のスレッドプール実装を見つけることができるかもしれません。
また
新しいThread()。Start()
プログラムを閉じても死なない前景スレッドを生成します。 ThreadPoolスレッドは、アプリを閉じると死ぬバックグラウンドスレッドです。
これらの相対的なリソース使用量に興味があり、Windows 8で.net 4.0リリースビルドを使用して2012年のデュアルコアIntel i5ラップトップでベンチマークを実行しました。スレッドプールは、スレッドがかかった場所から平均0.035ms平均5.06ms。言い換えれば、プール内のスレッドは、多数の短い存続スレッドに対して約300倍速く起動しました。少なくともテストされた範囲(100〜2000)のスレッドでは、スレッドごとの合計時間はかなり一定のように見えました。
これはベンチマークされたコードです:
for (int i = 0; i < ThreadCount; i++) {
Task.Run(() => { });
}
for (int i = 0; i < ThreadCount; i++) {
var t = new Thread(() => { });
t.Start();
}
以前のスレッドについてはこちらをご覧ください:
要約すると、多数の短命のスレッドを生成する必要がある場合はThreadpoolが適していますが、Threadsを使用するともう少し制御できます。
スレッドローカルストレージは、スレッドプールではお勧めできません。スレッドに「アイデンティティ」を付与します。すべてのスレッドが等しくなるわけではありません。現在、スレッドプールは、作成のオーバーヘッドなしで作業を行う準備ができている同一のスレッドの束が必要な場合に特に便利です。
多数のスレッドが必要な場合は、おそらくThreadPoolを使用する必要があります。スレッドを再利用して、スレッド作成のオーバーヘッドを節約します。
何かを行うために1つのスレッドだけが必要な場合は、おそらくThreadが最も簡単です。
adpoolスレッドの主な必要性は、ほぼ瞬時に完了すると予想される短い小さなタスクを処理することです。ハードウェア割り込みハンドラーは、多くの場合、カーネル以外のコードには適さないスタッキングコンテキストで実行されますが、ハードウェア割り込みハンドラーは、ユーザーモードのI / O完了コールバックをできるだけ早く実行する必要があることに気付く場合があります。そのようなことを実行する目的で新しいスレッドを作成すると、大げさになりすぎます。 I / O完了コールバックまたは他の同様のものを実行するためにディスパッチできる事前作成されたスレッドをいくつか持つことは、はるかに効率的です。
このようなスレッドの重要な点は、I / O完了メソッドが常に本質的に瞬時に完了し、ブロックしない場合、そのようなメソッドを現在実行しているそのようなスレッドの数が少なくともプロセッサの数に等しい場合、唯一の方法です前述のメソッドのいずれかが終了する前に他のスレッドが実行される可能性があるのは、他のメソッドのいずれかがブロックするか、その実行時間が通常のスレッド化タイムスライスを超える場合です。スレッドプールが意図したとおりに使用されている場合、これらのどちらも頻繁に発生することはありません。
メソッドの実行開始から100ミリ秒以内に終了することが期待できない場合、メソッドはメインスレッドプール以外の手段で実行する必要があります。 CPUを集中的に使用するがブロックしない多くのタスクを実行する場合は、「メイン」とは別のアプリケーションスレッドのプール(CPUコアごとに1つ)を使用してディスパッチすると便利です。スレッドプール。コアを超えるスレッドを使用すると、CPUを集中的にブロックしないタスクを実行するときに逆効果になるためです。ただし、メソッドの実行に1秒以上かかり、そのほとんどの時間がブロックされる場合、メソッドは専用スレッドで実行される可能性が高く、ほぼ確実にメインスレッドプールスレッドでは実行されません。 I / Oコールバックなどによって長時間実行操作をトリガーする必要がある場合、コールバックの前に長時間実行操作のスレッドを開始し、コールバックがパルスするモニターで待機させるか、またはコールバックが終了する間にコールバックに新しいスレッドを起動させて操作を実行させ、独自のスレッドをスレッドプールに効果的に返します。
一般的に(.NETを使用したことはありません)、スレッドプールはリソース管理の目的で使用されます。ソフトウェアに制約を設定できます。また、新しいスレッドの作成にはコストがかかる可能性があるため、パフォーマンス上の理由で行われることもあります。
システム固有の理由もあります。 Java(これが.NETに当てはまるかどうかわかりません)では、スレッドのマネージャーは、各スレッドがプールからプルされるときにスレッド固有の変数を適用し、返されるときにそれらの設定を解除します( ID)。
制約の例: 10個のdb接続しかないため、データベースへのアクセスには10個のワーカースレッドのみを許可します。
これは、独自のスレッドを作成すべきではないという意味ではありませんが、プールを使用する意味がある条件があります。
作成するスレッドの数がわからない場合や制御できない場合は、プールを使用することをお勧めします。
リストコントロールのpositionchangedイベントでデータベースから一部のフィールドを更新するためにスレッドを使用するフォームに問題があります(freezを避けます)。ユーザーがリストの位置をあまりにも速く変更していたため、ユーザーがデータベースからエラーを取得するまでに5分かかりました(Accessとの接続が多すぎる)...
基本的な問題を解決する他の方法(アクセスを使用しないことを含む)があることは知っていますが、プーリングは良いスタートです。