10

可能な重複:

Parallel.ForEachとTask.Factory.StartNew

私は約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


  • @ zooone9243:多分私はちょっと苦労していますが、これは単なる言語の問題ですが、4コアのマシンでは4つのスレッドしか同時に実行されません。 1,000個のスレッドを作成することはできます(メモリスペースが足りない場合)。ただし、適切な解決策はプールからスレッドを使用することです。意図したとおりに。それは約1,000のスレッドを実行する必要があります私を混乱させる部分。 - Martin Liversage
  • 説明しない限り、非同期I / Oは選択肢にならないことを教えてください。これはX / Y問題聞いたことがある場合非同期I / Oはこの種のタスクを実行するための正しい方法あなたのケースに当てはまらないと確信している場合は、説明する私たちが実際に最善の解決策を提供しようとすることができるようにあなたの問題。 - Aaronaught
  • それを修正します。これはアーキテクチャの問題であり、パフォーマンスの問題ではありません。 TPL(両方を含む)を使用しても許容できるパフォーマンスは得られません。TaskそしてParallel)せいぜい2つのうち小さいほうの中から選択するよう求めているだけです悪。 - Aaronaught
  • 余談ですが、これは古い質問です:Task / Parallelは4.0の機能です。非同期は4.5の機能です(はい、CTPがあったことに気付きました)。ですから、実際の4.0の機能だけがコードに含まれることを神から命じることができます。あるいは質問者が使用しなければならないWebサービスライブラリについて言及しているように、それは第三者であるかもしれず、それを修正し、すべてを非同期/装飾で飾ることはできません。 - Mike
  • ここで注意すべきこと - もしあなたが長時間実行(I / Oバウンドタスク)でParallel.ForEachを使うなら、スレッドスケジューラは焦ります。進行が遅いのは、タスクがCPUに過度に集中していることが原因であると想定しているため、スレッドプールに2 /分の速度でスレッドを追加し始めます。これは基本的に「リーク」します。並列foreachが完了するまで、この方法でスレッド化します。 - Steven Padfield

3 답변


35

おそらくあなたはこれに気づいていないのですが、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を意味し、スケジューラにスレッドの切り替えを処理させることを明確にする必要があります)、それらをプロファイルして最適なものを判断します。あなたの特定のユースケースあなたは自分自身を売っています。


  • 素晴らしい回答をありがとう。 ParallelクラスがTaskオブジェクトを作成すると、どのようにしてフォアグラウンドスレッドを作成でき、バックグラウンドスレッドを作成し、フォアグラウンドスレッドを作成するオプションが表示されないようなタスクライブラリを作成できるのでしょうか。 - Zaid Masud
  • @ zooone9243 - 実際にはフォアグラウンドスレッドを作成していません。代わりに、Wait()を呼び出すだけで、終了またはキャンセルされるまで実行をブロックします。 - Christopher Currens
  • @ zooone9243 - 私よりも少し複雑になっています。内部の仕組みをよく理解したい場合は、こちらをチェックしてください。.NET参照ソース - Christopher Currens

4

どちらの方法もあなたのシナリオにはまったく不適切です。

TaskCreationOptions.LongRunningTPLのように、CPUに依存しないタスクには確かに良い選択です(Parallelクラス/エクステンション)は、ほとんどの場合(スレッドではなく)複数のコア上で実行することによって、CPUバウンド操作のスループットを最大化するためのものです。

ただし、1000タスクというのはこれには受け入れられません。それらがすべて同時に実行されているかどうかは、まさに問題ではありません。同期I / Oを待機している100スレッドでさえも、許容できない状況です。コメントの1つが示唆しているように、あなたのアプリケーションは莫大な量のメモリを使用し、コンテキスト切り替えにその時間のほぼ全時間を費やすことになります。 TPLはこの規模に合わせて設計されていません。

操作がI / Oに制限されている場合 - そしてWebサービスを使用している場合 - 彼らです - 非同期I / Oは正しい解決方法ではありません。のみ溶液。あなたがあなたのコードのいくつかを再設計しなければならないならば(例えば、もともと何もなかったところで主要なインタフェースに非同期メソッドを追加するなど)、やるI / O完了ポートはのみこの特定のタイプの並行性を適切にサポートできるWindowsまたは.NETのメカニズム。

私は非同期I / Oがどういうわけか「オプションではない」という状況について聞いたことがありません。私はこの制約のための有効な使用例さえ想像できません。あなたが非同期I / Oを使用することができないならば、これは直さなければならない重大な設計問題を示すでしょう、できるだけ早く


  • 「非同期I / Oがどういうわけか選択肢になっていなかったという状況については、これまで聞いたことがありません」。 ...各Webサービスの呼び出しには高価なハンドシェイクが必要です。接続を確立することは、実際の呼び出し自体よりもはるかに重要です。 IO完了ポートに関する私の知識は限られていますが、このシナリオに使用できますか?あなたがこれらについてのよい参照があるならば、共有してください。ありがとう。 - Zaid Masud
  • @ zooone9243、なぜそれが非同期IOを使用できないという意味にならないのかわかりません。あなたが私たちにもっと詳しく言わない限り、それをどのように正確に行うべきかをあなたに言うのは難しいです。 - svick
  • @svickこれらのI / O完了ポートについてもっと学ぶ必要があります...ここで説明しているように、アンマネージWindows I / Oスレッドを使用することについて話していますか?blogs.msdn.com/b/ericeil/archive/2008/06/20/… - Zaid Masud
  • @ zooone9243、いいえ、使用することを意味しますBeginXxx()/EndXxx()単なるメソッドではなくメソッドXxx()方法。私たちが話しているのはどの方法なのか、あなたがしていることにかかっています。WebRequestまたはSocketまたは他の何か)。のBegin/Endその後、メソッドは内部的に入出力完了ポートを使用します。 - svick
  • @ zooone9243:おそらく、「Webサービス」の意味を誤って解釈しています。ここでは、私の経験では、Webサービスの全体的な前提は、それが標準のWebプロトコルとフォーマット(つまり、SOAP、XML、JSON、すべてHTTPまたはHTTPS)を使用することです。しないアクセスするには独自のライブラリが必要です。これは完全に不透明なバイナリエンコードされたRPCサービスで、ソースや仕様はありませんか? - Aaronaught

4

これは直接の比較ではありませんが、役に立つと思います。私はあなたが説明したのと同じようなことをします(私の場合私はREST呼び出しを提供しているもう一方の端に負荷分散されたサーバークラスタがあることを知っています)。使用して良い結果が得られますParrallel.ForEach最適な数のワーカースレッドをスピンアップする私はまた、次のコードを使用することを条件に私のオペレーティングシステムに伝えるために、それは通常より多くのエンドポイントに接続することができます。

    var servicePointManager = System.Net.ServicePointManager.FindServicePoint(Uri);
    servicePointManager.ConnectionLimit = 250;

あなたが接続するそれぞれのユニークなURLのために一度それを呼ばなければならないことに注意してください。

リンクされた質問


関連する質問

最近の質問