回答:
一定の処理を必要とする多数の論理タスクがあり、それを並行して実行する場合は、プール+スケジューラーを使用します。
リモートサーバーからのデータのダウンロードやディスクアクセスなど、IO関連のタスクを同時に実行する必要があるが、これを数分ごとに行う必要がある場合は、独自のスレッドを作成し、終了したらスレッドを強制終了します。
編集:いくつかの考慮事項について、データベースアクセス、物理/シミュレーション、AI(ゲーム)、および多数のユーザー定義タスクを処理する仮想マシンで実行されるスクリプトタスクにスレッドプールを使用しています。
通常、プールはプロセッサごとに2つのスレッドで構成されます(現在では4つ)が、必要なスレッド数がわかっていれば、必要な数のスレッドを設定できます。
編集:独自のスレッドを作成する理由は、コンテキストの変更が原因です(スレッドがメモリと一緒にプロセスとの間でスワップする必要がある場合)。スレッドを使用していないときなど、意味のないコンテキストの変更があると、言うまでもないままにしておくと、プログラムのパフォーマンスが半分になります(3つのスリープスレッドと2つのアクティブスレッドがあるとします)。したがって、これらのダウンロードスレッドが待機しているだけの場合、大量のCPUを消費し、実際のアプリケーションのキャッシュを冷却しています。
.Netのスレッドプールの概要は次のとおりです。http://blogs.msdn.com/pedram/archive/2007/08/05/dedicated-thread-or-a-threadpool-thread.aspx
投稿には、スレッドプールを使用してはならず、代わりに独自のスレッドを開始する必要がある場合のポイントもいくつかあります。
この無料の電子書籍を読むことを強くお勧めします 。JosephAlbahariによるThreading in C#
少なくとも「はじめに」セクションを読んでください。この電子書籍には優れた紹介があり、豊富な高度なスレッド情報も含まれています。
スレッドプールを使用するかどうかを知ることは、ほんの始まりにすぎません。次に、スレッドプールに入る方法がニーズに最も適しているかどうかを判断する必要があります。
この電子書籍は、これらすべてを説明し、それらを使用する場合と独自のスレッドを作成する場合をアドバイスします。
スレッドプールは、スレッド間のコンテキストの切り替えを減らすように設計されています。複数のコンポーネントが実行されているプロセスを考えます。これらの各コンポーネントがワーカースレッドを作成している可能性があります。プロセス内のスレッドが多いほど、コンテキストの切り替えに費やされる時間が多くなります。
ここで、これらの各コンポーネントがアイテムをスレッドプールにキューイングする場合、コンテキスト切り替えのオーバーヘッドが大幅に減少します。
スレッドプールは、CPU(またはCPUコア)全体で実行される作業を最大化するように設計されています。そのため、デフォルトでは、スレッドプールはプロセッサごとに複数のスレッドを起動します。
スレッドプールを使用したくない状況がいくつかあります。I / Oを待機している場合や、イベントを待機している場合などは、そのスレッドプールのスレッドを拘束し、他のユーザーが使用することはできません。同じアイデアが長時間実行タスクに適用されますが、長時間実行タスクを構成するものは主観的です。
Pax Diabloも良い点を挙げています。スレッドのスピンアップは無料ではありません。時間がかかり、スタックスペースのために追加のメモリを消費します。スレッドプールはこのコストを償却するためにスレッドを再利用します。
注:スレッドプールスレッドを使用してデータをダウンロードしたり、ディスクI / Oを実行したりすることについて質問しました。これにはスレッドプールスレッドを使用しないでください(上記で概要を説明した理由によります)。代わりに非同期I / O(別名BeginXXおよびEndXXメソッド)を使用します。以下のためにFileStream
それだろうBeginRead
とEndRead
。用HttpWebRequest
されることBeginGetResponse
とEndGetResponse
。使い方はもっと複雑ですが、マルチスレッドI / Oを実行する適切な方法です。
処理の重要、可変、または不明な部分をブロックする可能性がある操作の.NETスレッドプールには注意してください。スレッドが不足する傾向があるためです。.NET並列拡張の使用を検討してください。これにより、スレッド化された操作に対して多数の論理的な抽象化が提供されます。また、新しいスケジューラーも含まれています。これは、ThreadPoolを改善したものです。こちらをご覧ください
アプリケーションの存続期間全体など、長期間存続するバックグラウンドタスクがある場合は、独自のスレッドを作成するのが妥当です。スレッドで実行する必要がある短いジョブがある場合は、スレッドプーリングを使用します。
多くのスレッドを作成するアプリケーションでは、スレッドを作成するオーバーヘッドがかなり大きくなります。スレッドプールを使用すると、スレッドが1回作成されて再利用されるため、スレッド作成のオーバーヘッドが回避されます。
私が取り組んだアプリケーションでは、スレッドの作成から、存続期間の短いスレッドのスレッドプールを使用するように変更することで、アプリケーションのスループットが本当に向上しました。
同時実行ユニットで最高のパフォーマンスを得るには、独自のスレッドプールを作成します。ここでは、起動時にThreadオブジェクトのプールが作成され、ブロッキング(以前は中断)になり、実行されるコンテキスト(標準インターフェースが実装されたオブジェクト)を待機します。あなたのコード)。
タスクとスレッド、.NET ThreadPoolに関する多くの記事では、パフォーマンスの決定に必要なものを実際に提供できていません。しかし、それらを比較すると、スレッド、特にスレッドのプールが勝っています。CPU全体に最適に分散されており、起動が速くなります。
議論すべきことは、Windows(Windows 10を含む)の主要な実行単位はスレッドであり、OSコンテキスト切り替えのオーバーヘッドは通常無視できるという事実です。簡単に言うと、コンテキストの切り替えを保存することでパフォーマンスが向上するか、CPU使用率が向上するかを問わず、これらの記事の多くの説得力のある証拠を見つけることができませんでした。
少しリアリズムを取りましょう:
私たちのほとんどは、アプリケーションが確定的である必要はありません。また、ほとんどの場合、たとえばオペレーティングシステムの開発に伴う、スレッドのハードノックバックグラウンドを持っていません。上に書いたのは初心者向けではありません。
したがって、最も重要なのは、プログラムするのが簡単なことについて話し合うことです。
独自のスレッドプールを作成する場合は、実行ステータスの追跡、一時停止と再開のシミュレーション方法、実行のキャンセル方法(アプリケーション全体を含む)に注意する必要があるため、少し書く必要があります。シャットダウン。また、プールを動的に拡張するかどうか、およびプールの容量制限についても考慮する必要がある場合があります。私はそのようなフレームワークを1時間で書くことができますが、それは私が何度も書いたためです。
実行ユニットを作成する最も簡単な方法は、タスクを使用することでしょう。タスクの優れた点は、タスクを作成してコード内でインラインで開始できることです(ただし、注意が必要な場合があります)。タスクをキャンセルするときに処理するキャンセルトークンを渡すことができます。また、promiseアプローチを使用してイベントを連鎖させ、特定のタイプの値を返すようにすることができます。さらに、非同期と待機により、より多くのオプションが存在し、コードの移植性が向上します。
基本的に、タスクとスレッド、.NET ThreadPoolの長所と短所を理解することが重要です。高いパフォーマンスが必要な場合は、スレッドを使用し、自分のプールを使用することを好みます。
比較する簡単な方法は、512スレッド、512タスク、および512スレッドプールスレッドを起動することです。スレッドでは最初に遅延が発生します(したがって、なぜスレッドプールを書き込むのか)が、タスクと.NET ThreadPoolスレッドがすべて開始するまでに数分かかる間、512スレッドすべてが数秒で実行されます。
以下は、このようなテスト(16 GBのRAMを搭載したi5クアッドコア)の結果で、30秒ごとに実行されます。実行されるコードは、SSDドライブで単純なファイルI / Oを実行します。
スレッドプールは、使用可能なスレッドよりも多くのタスクを処理する場合に最適です。
すべてのタスクをスレッドプールに追加し、特定の時間に実行できるスレッドの最大数を指定できます。
MSDNのこのページを確認してください:http : //msdn.microsoft.com/en-us/library/3dasc8as(VS.80).aspx
バックグラウンドワーカーを調査することを忘れないでください。
私は多くの状況で私は重労働なしで私が欲しいものだけを与えてくれます。
乾杯。
私は通常、別のスレッドで何かを実行する必要があるときはいつでもThreadpoolを使用しますが、実行または終了するタイミングを気にしません。ログのようなもの、またはファイルをバックグラウンドでダウンロードしているようなもの(非同期スタイルを実行するためのより良い方法があります)。さらに制御が必要な場合は、自分のスレッドを使用します。また、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などのタスクに役立ちます。