私は約1,000のタスクを実行する必要がありますThreadPool
毎晩(将来的には数が増える可能性があります)。各タスクは長時間実行されている操作(Webサービスからのデータの読み取り)を実行しています。CPUに負荷をかけない。Async I/O
この特定のユースケースではオプションではありません。
与えられたIList<string>
パラメータのDoSomething(string x)
。私は次の2つの選択肢から選ぶようにしています:
IList<Task> tasks = new List<Task>();
foreach (var p in parameters)
{
tasks.Add(Task.Factory.StartNew(() => DoSomething(p), TaskCreationOptions.LongRunning));
}
Task.WaitAll(tasks.ToArray());
または
Parallel.ForEach(parameters, new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount*32}, DoSomething);
どのオプションが優れているのか
注意 :
答えは使用の間の比較を含むべきですTaskCreationOptions.LongRunning
そしてMaxDegreeOfParallelism = Environment.ProcessorCount * SomeConstant
。
おそらくあなたはこれに気づいていないのですが、Parallel
クラスは単に(複雑な)ラッパーです。Task
オブジェクトご参考までに、Parallel
クラスを作成しますTask
を持つオブジェクトTaskCreationOptions.None
。しかしMaxDegreeOfParallelism
どの作成オプションがタスクオブジェクトのコンストラクタに渡されても、それらのタスクオブジェクトに影響します。
TaskCreationOptions.LongRunning
基礎となるものに「ヒント」を与えるTaskScheduler
スレッドのオーバーサブスクリプションを使用するとパフォーマンスが向上する可能性があります。 I / Oなど、待ち時間が長いスレッドにはオーバーサブスクリプションが適しています。これは、1つのコアに複数のスレッド(タスクではなく、スレッド)を割り当てて、待機するのではなく常に実行するためです。スレッドが待機状態にある間に完了する操作。にTaskScheduler
それを使用してThreadPool
、 それ意志LongRunningタスクを専用のスレッドで実行する(タスクごとにスレッドがある唯一の場合)。それ以外の場合は、スケジュールと作業の盗用を伴いながら、通常どおり実行されます(実際には、ここで必要なもの)。
MaxDegreeOfParallelism
実行される並行操作の数を制御します。データが分割され、そこから処理される分割の最大数を指定するのに似ています。もしTaskCreationOptions.LongRunning
指定できた場合、これは、一度に実行されるタスクの数を制限することになります。TaskScheduler
最大同時実行レベルがその値に設定されているこの例に似ています。
あなたが欲しいかもParallel.ForEach
。ただし、追加MaxDegreeOfParallelism
このような高い数に等しい場合、タスクはまだタスクによって制御されるため、実際には同時に多数のスレッドが実行されることが保証されません。ThreadPoolTaskScheduler
。そのスケジューラは一度に実行されるスレッドの数を可能な限り最小の量にするでしょう、それは私が2つの方法の間の最大の違いであると思います。あなたはあなた自身で書く(そして指定する)ことができますTaskScheduler
それは並列度の振る舞いの最大度を模倣し、そして両方の長所を持っているでしょう、しかし私はあなたがしていることに興味があることを疑っています。
私が思うに、待ち時間や実際に行う必要のあるリクエストの数によっては、タスクの使用は多くの場合(?)のパフォーマンスが向上しますが、並行して使用するリソースの使用量はより多くなります。もちろん、非同期I / Oのパフォーマンスはこれら2つのオプションのいずれよりもはるかに優れていますが、従来のライブラリを使用しているため、これを実行できないことは理解できます。したがって、残念ながら、どちらを選択しても平凡なパフォーマンスが得られません。
本当の解決策は非同期I / Oが起こるようにする方法を考え出すことでしょう。状況が分からないので、それ以上に役に立つとは思えません。プログラム(read、thread)は実行を継続し、カーネルはI / O操作が完了するのを待ちます(これはI / O完了ポートの使用とも呼ばれます)。スレッドは待機状態ではないため、ランタイムはより少ないスレッドでより多くの作業を行うことができ、通常はコア数とスレッド数の間の最適な関係になります。私が望むのと同じくらい多くのスレッドを追加しても、パフォーマンスが向上するわけではありません(実際には、コンテキストの切り替えなどの理由でパフォーマンスが低下することがよくあります)。
しかし、この答え全体は、決定的な意味では役に立ちません。最後のあなたの質問に答えてください、私はそれがあなたにいくつかの必要な方向性を与えることを願いますが。プロファイルするまでは、パフォーマンスが良くなることがわかりません。両方とも試していない場合(私はLongRunningオプションなしのTaskを意味し、スケジューラにスレッドの切り替えを処理させることを明確にする必要があります)、それらをプロファイルして最適なものを判断します。あなたの特定のユースケースあなたは自分自身を売っています。
どちらの方法もあなたのシナリオにはまったく不適切です。
TaskCreationOptions.LongRunning
TPLのように、CPUに依存しないタスクには確かに良い選択です(Parallel
クラス/エクステンション)は、ほとんどの場合(スレッドではなく)複数のコア上で実行することによって、CPUバウンド操作のスループットを最大化するためのものです。
ただし、1000タスクというのはこれには受け入れられません。それらがすべて同時に実行されているかどうかは、まさに問題ではありません。同期I / Oを待機している100スレッドでさえも、許容できない状況です。コメントの1つが示唆しているように、あなたのアプリケーションは莫大な量のメモリを使用し、コンテキスト切り替えにその時間のほぼ全時間を費やすことになります。 TPLはこの規模に合わせて設計されていません。
操作がI / Oに制限されている場合 - そしてWebサービスを使用している場合 - 彼らです - 非同期I / Oは正しい解決方法ではありません。のみ溶液。あなたがあなたのコードのいくつかを再設計しなければならないならば(例えば、もともと何もなかったところで主要なインタフェースに非同期メソッドを追加するなど)、やるI / O完了ポートはのみこの特定のタイプの並行性を適切にサポートできるWindowsまたは.NETのメカニズム。
私は非同期I / Oがどういうわけか「オプションではない」という状況について聞いたことがありません。私はこの制約のための有効な使用例さえ想像できません。あなたが非同期I / Oを使用することができないならば、これは直さなければならない重大な設計問題を示すでしょう、できるだけ早く。
BeginXxx()
/EndXxx()
単なるメソッドではなくメソッドXxx()
方法。私たちが話しているのはどの方法なのか、あなたがしていることにかかっています。WebRequest
またはSocket
または他の何か)。のBegin
/End
その後、メソッドは内部的に入出力完了ポートを使用します。 - svick
これは直接の比較ではありませんが、役に立つと思います。私はあなたが説明したのと同じようなことをします(私の場合私はREST呼び出しを提供しているもう一方の端に負荷分散されたサーバークラスタがあることを知っています)。使用して良い結果が得られますParrallel.ForEach
最適な数のワーカースレッドをスピンアップする私はまた、次のコードを使用することを条件に私のオペレーティングシステムに伝えるために、それは通常より多くのエンドポイントに接続することができます。
var servicePointManager = System.Net.ServicePointManager.FindServicePoint(Uri);
servicePointManager.ConnectionLimit = 250;
あなたが接続するそれぞれのユニークなURLのために一度それを呼ばなければならないことに注意してください。
Task
そしてParallel
)せいぜい2つのうち小さいほうの中から選択するよう求めているだけです墓悪。 - Aaronaught