2010-12-02 8 views
10

私はParallel.Forループを7500を少し上回るオブジェクトで実行しています。そのforループの中で、私はこれらのオブジェクトのそれぞれに対して、特に2つのWebサービスと2つの内部メソッドを呼び出すいくつかのことをしています。 Webサービスは単にオブジェクトを検査し、処理して文字列を返します。それをオブジェクトのプロパティとして設定します。同じ2つの内部メソッドにも同じです。Parallel.For約1370反復後にフリーズする理由は分かりません

私はディスクに何も書き込んでいない、またはディスクから読み込みません。

また、winformsアプリケーションのUIをラベルと進行状況バーで更新して、どこにいるのかをユーザーに知らせることもできます。ここでは、コードです:

var task = Task.Factory.StartNew(() => 
{ 
    Parallel.For(0, upperLimit, (i, loopState) => 
    { 
    if (cancellationToken.IsCancellationRequested) 
     loopState.Stop(); 
    lblProgressBar.Invoke(
     (Action) 
     (() => lblProgressBar.Text = string.Format("Processing record {0} of {1}.", (progressCounter++), upperLimit))); 
    progByStep.Invoke(
     (Action) 
     (() => progByStep.Value = (progressCounter - 1))); 

     CallSvc1(entity[i]); 
     Conversion1(entity[i]); 
     CallSvc2(entity[i]); 
     Conversion2(entity[i]); 
    }); 
}, cancellationToken); 

これはwin7の32ビットマシン上で行われています。

なぜインクリメンタが約1370(1361、1365、1371)になったときに、突然これが凍結するかについてのアイデアはありますか?

これをどのようにデバッグして何がロックされているかを知るためのアイデアはありますか?

EDIT:
以下のコメントにいくつかの答え:
@BrokenGlass - いや、いや相互運用。 x86コンパイルを試してみましょう。

@chibacity - バックグラウンドタスクであるため、UIがフリーズしていません。フリーズするまでは、プログレスバーとラベルは約2秒ごとに表示されます。それが凍ったら、動くのを止めるだけです。私はそれが停止した番号が処理されたことを確認できますが、それ以上のことは確認できません。デュアルコア2.2GHzでのCPU使用率は、動作中はそれぞれ3〜4%、1〜2%は凍結すると最小限に抑えられます。

@Henk Holterman - 1360になるまでには10-12分かかります。はい、これらのレコードはすべて処理されたが、残りのレコードは処理されていないことが確認できます。

@CodeInChaos - ありがとう、私はそれを試してみよう!このコードは、私が並行して取り出した場合には機能しますが、それはちょうど永遠と一日かかるのです。私はスレッドの数を制限しようとはしませんでしたが、そうです。

EDIT 2:
Webサービスで何が起こっているかを基本的にWebサービス

で何が起こっているかのようないくつかの詳細は、彼らはいくつかのデータを渡すと、データ(XmlNodeの)を受け取るということです。そのノードはConversion1プロセスで使用され、次にCallSvc2メソッドなどに送信されるエンティティに別のプロパティを設定します。これは次のようになります。

private void CallSvc1(Entity entity) 
{ 
    var svc = new MyWebService(); 
    var node = svc.CallMethod(entity.SomeProperty); 
    entity.FieldToUpdate1.LoadXml(node.InnerXml); 
} 
private void Conversion1(Entity entity) 
{ 
    // Do some xml inspection/conversion stuff 
    if (entity.FieldToUpdate1.SelectSingleNode("SomeNode") == "something") { 
     entity.FieldToUpdate2 = SomethingThatWasConverted; 
    } 
    else { 
     // Do some more logic 
    } 
} 
private void CallSvc2(Entity entity) 
{ 
    var svc = new SomeOtherWebService(); 
    var xmlNode = svc.MethodToCall(entity.FieldToUpdate2.InnerXml); 
    entity.AnotherXmlDocument.LoadXml(xmlNode.InnerXml); 
} 

ご覧のとおり、わかりやすいものです。いくつかの変換方法では多くのことが起こっていますが、どれもブロックされてはいけません。また、以下に述べるように、すべてがWebサービス呼び出しに載っている「待機中」状態にある1024のスレッドがあります。私はここでhttp://www.albahari.com/threading/を読んで、MaxThreadsは32ビットマシン上の.Net 4に対して1023にデフォルト設定されていると読んでいます。

私がここにいることを考えれば、待機中のスレッドをどのように解放できますか?

+0

前にも同様の問題がありました。それが何か変わるかどうかを確認するために、x86モードでプロジェクトを構築しようと考えていました。あなたはあなたの仕事でどんなInterOpもやらないでしょう? – BrokenGlass

+0

凍っているのか、それとも非常に遅いのですか? CPU使用率はどのくらいですか? –

+0

まだTPLで作業していませんが、デバッガを起動して、どのメソッドが呼び出されたのかを確認することはできません。通常のforループに置き換えるとコードは機能しますか? 'Parallel.For'を使用して1つまたは2つのスレッドに制限するとどうなりますか? – CodesInChaos

答えて

9

考えられる説明:スレッドを作成できない状態にプロセスがあり、処理が進まないため、すべてが停止してしまいます。

確かに、その仮説が正しいかどうかに関わらず、これとは全く異なるアプローチをとる必要があります。 Parallel.Forはこれを解決するための間違った方法です。 (ParallelはCPUバインドされた作業に最も適していますが、ここではIOバインドされた作業があります)。進行中のWebサービス要求が何千もある場合は、マルチスレッドコードではなく非同期コードを使用する必要があります。 。非同期APIを使用すると、ほんの一握りのスレッドを使用しながら同時に何千もの要求を開始することができます。

これらの要求を実際に同時に実行できるかどうかは、別の問題です。現在の「スレッドアポカリプス」実装を使用するか、より効率的な非同期実装を使用する場合でも、スロットルを実行する可能性があります。 (.NETでは、実際に要求するリクエストの数を制限することがあります)。多くのリクエストを好きなだけ作ることができますが、ほとんどのリクエストは完了するまで待っています。例えば。私はWebRequestがただ1つのドメインへの並行接続をわずか2に制限していると思います... 1000+スレッド(または1000+非同期要求)を起動すると、ロード要求が2つの現在の要求の1つになるのを待っています。

独自のスロットルを行う必要があります。一度にいくつの未処理要求を同時に実行するかを決定し、一度に多くの要求を開始する必要があります。 Parallelにできるだけ早くできるだけ多くのものを立ち上げて、すべてのものを駄目にするように頼んでください。

追記

クイックフィックスはParallelOptionsオブジェクトを受け入れParallel.Forのオーバーロードを使用するかもしれない - あなたは同時要求の数を制限するために、そのMaxDegreeOfParallelismプロパティを設定することができます。これにより、実際にスレッドが不足しているスレッドの実装が中断されます。しかし、この問題に対する非効率的な解決方法は残っています。 (そして、私が知っている限り、実際に何千もの同時リクエストを行う必要があります。たとえば、Webクローラを作成する場合、それは実際にやりたいことです。Parallelは、使用しているWebサービスプロキシがAPM(BeginXxx、EndXxx)をサポートしている場合は、Taskオブジェクトにラップアップできます。Task.TaskFactoryは、進行中の非同期操作を表すタスクを提供するFromAsyncを提供します。

しかし、何千ものリクエストを一度に飛行させようとするなら、スロットル戦略について慎重に考える必要があります。できるだけ早くリクエストを投げれば、最適な戦略にはなりません。

+0

私が消費しようとしているWebサービスがAPM(BeginXXXとEndXXX)をサポートしていないのに、SvcNameCompletedイベントハンドラとSvcNameAsyncを持っている場合、TaskFactory FromAsyncをまだ使用できますか? –

+0

いいえ、TaskFactory.FromAsyncはAPM専用に設計されています。ただし、http://msdn.microsoft.com/library/dd997423を参照すると、「複雑なEAP操作をタスクとして公開する」セクションに、XxxAsync/XxxCompletedパターンの処理方法が示されています。 (そのパターンが知られているように、EAP) –

+0

私はほとんどが更新部分に同意し、スレッド数は制限されるべきです。低い数では、N≦10程度である。そしてそれは非同期パターン全体の重要性を低下させます。 APMの方が効率的であるということについてはイアンが間違いなく正しいですが、この(大きな)タスクには少数のスレッドを使用するのはそれほど悪くありません。 –

5

VSデバッガでアプリを実行します。それがロックアップするように見えるとき、VSにDebug:Break Allを伝えてください。そして、Debug:Windows:Threadsに行き、プロセス内のスレッドを見てください。それらのうちのいくつかは、あなたの並列forループにあるスタックトレースを表示する必要があり、デバッガによってプロセスが停止されたときに何をしているのかを示します。

+0

デバッグスレッドウィンドウには1000を超えるスレッドがあり、スレッドの場所は2つのWebサービス呼び出しのいずれかになっています。彼らはすべてカテゴリの列に黄色とWorkerThreadを表示し、「眠っている、待っている、または参加している」と言います。これは問題であり、待っているスレッドの膨大な量ですか?もしそうなら、私はそれについて何ができますか?私は何かクラッシュや例外を投げて表示されません。 –

+0

それはかなり深刻ですね。パラレルループからビットをダイヤルバックしてみてください。まっすぐな順次ループとして機能することを確認してから、並列ループのスレッド数を制限してみてください。 – dthorpe

+0

「Parallel Tasks」ウィンドウを見ると、それはちょうど1024の待機中のスレッドであり、すべてがWebサービス呼び出しに置かれていることがわかります。これらのスレッドをプールに戻すことができない理由は不明です。非Webサービスコールスレッドはプールに返されます。 Hmmmmmmm –

関連する問題