私はC#でマルチスレッドプログラミングを学ぼうとしていますが、スレッドプールを使用するのが最適なのか、独自のスレッドを作成するのが最適なのかについて混乱しています。ある本では、小さなタスクにのみスレッドプールを使用することを推奨していますが(それが意味するものは何でも)、実際のガイドラインを見つけることができないようです。
スレッドプールと独自のスレッドの作成の長所と短所は何ですか?そして、それぞれのユースケースの例は何ですか?
私はC#でマルチスレッドプログラミングを学ぼうとしていますが、スレッドプールを使用するのが最適なのか、独自のスレッドを作成するのが最適なのかについて混乱しています。ある本では、小さなタスクにのみスレッドプールを使用することを推奨していますが(それが意味するものは何でも)、実際のガイドラインを見つけることができないようです。
スレッドプールと独自のスレッドの作成の長所と短所は何ですか?そして、それぞれのユースケースの例は何ですか?
他の言語と同じ理由で、C#でスレッドプールを使用することをお勧めします。
実行中のスレッドの数を制限したい場合、またはスレッドの作成と破棄のオーバーヘッドを望まない場合は、スレッドプールを使用します。
小さな仕事とは、あなたが読む本は寿命の短い仕事を意味します。1秒間だけ実行されるスレッドを作成するのに10秒かかる場合、それはプールを使用する必要がある1つの場所です(私の実際の数値は無視してください。重要なのは比率です)。
そうしないと、スレッドの作成と破棄に多くの時間を費やし、スレッドが意図した作業を単に実行するのではありません。
一定の処理を必要とする論理タスクが多数あり、それを並行して実行したい場合は、pool+schedulerを使用してください。
リモートサーバーからのダウンロードやディスクアクセスなど、IO関連のタスクを同時に実行する必要があるが、これを数分に1回実行する必要がある場合は、独自のスレッドを作成し、終了したらそれらを強制終了します。
編集:いくつかの考慮事項について、データベースアクセス、物理/シミュレーション、AI(ゲーム)、および多くのユーザー定義タスクを処理する仮想マシンで実行されるスクリプトタスクにスレッドプールを使用します。
通常、プールはプロセッサごとに2つのスレッドで構成されます(現在は4つになる可能性があります)が、必要なスレッド数がわかっている場合は、必要なスレッド数を設定できます。
編集:独自のスレッドを作成する理由は、コンテキストの変更によるものです(スレッドがプロセスの内外で、メモリとともにスワップインおよびスワップアウトする必要がある場合)。無駄なコンテキスト変更があると、たとえば、スレッドを使用していないときは、スレッドをそのままにしておくと、プログラムのパフォーマンスが簡単に半分になります(たとえば、3つのスリープスレッドと2つのアクティブスレッドがあるとします)。したがって、ダウンロードスレッドが待機しているだけの場合は、CPUを大量に消費し、実際のアプリケーションのキャッシュを冷却しています。
.Netのスレッドプールの概要は次のとおりです。http://blogs.msdn.com/pedram/archive/2007/08/05/dedicated-thread-or-a-threadpool-thread.aspx
投稿には、スレッドプールを使用せず、代わりに独自のスレッドを開始する必要がある場合についてもいくつかのポイントがあります。
この無料の電子書籍を読むことを強くお勧めします: JosephAlbahariによるC#でのスレッド化
少なくとも「はじめに」のセクションをお読みください。この電子書籍は、優れた紹介を提供し、高度なスレッド情報も豊富に含んでいます。
スレッドプールを使用するかどうかを知ることは、ほんの始まりに過ぎません。次に、スレッドプールに入る方法がニーズに最も適しているかどうかを判断する必要があります。
この電子書籍では、これらすべてについて説明し、いつ使用するか、独自のスレッドを作成するかについてアドバイスしています。
スレッド プールは、スレッド間のコンテキストの切り替えを減らすように設計されています。いくつかのコンポーネントが実行されているプロセスを考えてみましょう。これらの各コンポーネントは、ワーカー スレッドを作成している可能性があります。プロセス内のスレッドが多いほど、コンテキストの切り替えにより多くの時間が浪費されます。
ここで、これらの各コンポーネントが項目をスレッド プールのキューに入れていれば、コンテキスト切り替えのオーバーヘッドが大幅に削減されます。
スレッド プールは、CPU (または CPU コア) 全体で実行される作業を最大化するように設計されています。そのため、デフォルトでは、スレッド プールはプロセッサごとに複数のスレッドを起動します。
スレッド プールを使用したくない状況がいくつかあります。I/O を待機している場合やイベントを待機している場合などは、そのスレッド プール スレッドを拘束し、他のユーザーが使用することはできません。同じ考え方が長時間実行タスクにも当てはまりますが、何が長時間実行タスクを構成するかは主観的なものです。
パックス・ディアブロも良い点を挙げています。スレッドのスピンアップは無料ではありません。時間がかかり、スタック スペースのために追加のメモリを消費します。スレッド プールはスレッドを再利用して、このコストを償却します。
注: スレッド プール スレッドを使用してデータをダウンロードしたり、ディスク I/O を実行したりすることについて質問されました。これにはスレッド プール スレッドを使用しないでください (前述の理由により)。代わりに、非同期 I/O (別名、BeginXX および EndXX メソッド) を使用します。a の場合、FileStream
と にBeginRead
なりEndRead
ます。の場合はHttpWebRequest
と にBeginGetResponse
なりEndGetResponse
ます。使い方はより複雑ですが、マルチスレッド I/O を実行する適切な方法です。
処理の重要な部分、可変部分、または未知の部分をブロックする可能性がある操作の .NET スレッド プールに注意してください。スレッドが枯渇する傾向があるためです。.NET 並列拡張機能の使用を検討してください。これにより、スレッド化された操作に対して十分な数の論理抽象化が提供されます。また、ThreadPool を改善する新しいスケジューラも含まれています。こちらをご覧ください
小さなタスクにのみスレッド プールを使用する理由の 1 つは、スレッド プール スレッドの数が限られていることです。1 つが長時間使用されると、そのスレッドが他のコードによって使用されなくなります。これが何度も発生すると、スレッド プールが使い果たされる可能性があります。
スレッド プールを使い切ると微妙な影響が生じる可能性があります。たとえば、一部の .NET タイマーはスレッド プール スレッドを使用し、起動しません。
アプリケーションの存続期間全体のように、長期間存続するバックグラウンドタスクがある場合は、独自のスレッドを作成するのが妥当です。スレッドで実行する必要のある短いジョブがある場合は、スレッドプールを使用します。
多くのスレッドを作成しているアプリケーションでは、スレッド作成のオーバーヘッドがかなり大きくなります。スレッドプールを使用すると、スレッドが1回作成されて再利用されるため、スレッド作成のオーバーヘッドが回避されます。
私が取り組んだアプリケーションでは、スレッドの作成から短命のスレッドにスレッドプールを使用するように変更することで、アプリケーションのスループットが大幅に向上しました。
スレッドプールは、使用可能なスレッドよりも処理するタスクが多い場合に最適です。
すべてのタスクをスレッドプールに追加し、特定の時間に実行できるスレッドの最大数を指定できます。
MSDNのこのページを 確認してください:http: //msdn.microsoft.com/en-us/library/3dasc8as (VS.80).aspx
可能であれば、常にスレッドプールを使用し、可能な限り最高レベルの抽象化で作業してください。スレッドプールは、スレッドの作成と破棄を隠します。これは通常、良いことです。
スレッドを作成するという高価なプロセスを回避するため、ほとんどの場合、プールを使用できます。
ただし、シナリオによっては、スレッドを作成したい場合があります。たとえば、スレッドプールを使用しているのはあなただけではなく、作成したスレッドが (共有リソースの消費を避けるために) 存続期間が長い場合や、スレッドのスタックサイズを制御したい場合などです。
バックグラウンド ワーカーを調査することを忘れないでください。
多くの状況で、重いものを持ち上げることなく、私が望むものだけを与えてくれます。
乾杯。
私は通常、別のスレッドで何かをする必要があるときはいつでもスレッドプールを使用し、実行または終了するときは本当に気にしません。ロギングやファイルのバックグラウンドダウンロードのようなものです(ただし、非同期スタイルを実行するためのより良い方法があります)。さらに制御が必要な場合は、独自のスレッドを使用します。また、私が見つけたのは、スレッドセーフキュー(独自のハック)を使用して「コマンドオブジェクト」を格納することです。1つを超えるスレッドで処理する必要のあるコマンドが複数ある場合に便利です。したがって、Xmlファイルを分割して各要素をキューに入れ、複数のスレッドでこれらの要素の処理を実行することができます。私はC#に変換したようなキューをuni(VB.net!)に戻しました。特別な理由なしに以下に含めました(このコードにはいくつかのエラーが含まれている可能性があります)。
using System.Collections.Generic;
using System.Threading;
namespace ThreadSafeQueue {
public class ThreadSafeQueue<T> {
private Queue<T> _queue;
public ThreadSafeQueue() {
_queue = new Queue<T>();
}
public void EnqueueSafe(T item) {
lock ( this ) {
_queue.Enqueue(item);
if ( _queue.Count >= 1 )
Monitor.Pulse(this);
}
}
public T DequeueSafe() {
lock ( this ) {
while ( _queue.Count <= 0 )
Monitor.Wait(this);
return this.DeEnqueueUnblock();
}
}
private T DeEnqueueUnblock() {
return _queue.Dequeue();
}
}
}
私はスレッド プールがコア間で作業をできるだけ少ないレイテンシで分散することを望んでいましたが、それは他のアプリケーションとうまく動作する必要はありませんでした。.NET スレッド プールのパフォーマンスが期待できるほど良くないことがわかりました。コアごとに 1 つのスレッドが必要であることはわかっていたので、独自のスレッド プール代替クラスを作成しました。このコードは、別の StackOverflow の質問に対する回答として提供されています。
元の質問に関しては、スレッドプールは、反復計算を並列実行できる部分に分割するのに役立ちます (結果を変更せずに並列実行できると仮定します)。手動スレッド管理は、UI や IO などのタスクに役立ちます。