2012-09-26 5 views
34

私は、遅いウェブサービスを並行して呼びます。私はサービスから何らかの情報を得る必要があることがわかって初めて、物事は素晴らしかったです。しかし、どこに値を戻すかわかりません。私はデータベースに書き込むことができません、HttpContext.Currentは、Parallel.ForEachを使用して呼び出されたメソッド内でヌルのように見えますParallel.ForEachから戻り値を収集するにはどうすればよいですか?

以下は、サンプルプログラムです(あなたの考えでは、文字列連結)

using System; 
using System.Threading.Tasks; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     WordMaker m = new WordMaker(); 
     m.MakeIt(); 
    } 
    public class WordMaker 
    { 
     public void MakeIt() 
     { 
      string[] words = { "ack", "ook" }; 
      ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word)); 
      Console.WriteLine("Where did my results go?"); 
      Console.ReadKey(); 
     } 
     public string AddB(string word) 
     { 
      return "b" + word; 
     } 
    } 

} 
+0

Parallel.ForEach' 'の異なる過負荷は、あなたが何をしたいことがありますhttp://msdn.microsoft.com/en-us/library/ dd991486.aspx –

+0

残念ながら、それはあなたがそうすることができるものではありません。 'Parallel.Foreach()'は戻り値を記録するために構築されたものではありません。しかし、あなたの 'AddB'関数で' ref'パラメータを使うことをお勧めします。そうするかもしれない。 –

+0

@PhillipSchmidt:この例で使用されているオーバーロードではありません... –

答えて

44

あなたはここでそれを破棄しました。あなたは、おそらくのような何かをしたい

ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word)); 

、あなたがこのの終わりにコレクションのいくつかの並べ替えをしたい場合

ParallelLoopResult result = Parallel.ForEach(words, word => 
{ 
    string result = AddB(word); 
    // do something with result 
}); 

ConcurrentBag

var resultCollection = new ConcurrentBag<string>(); 
ParallelLoopResult result = Parallel.ForEach(words, word => 
{ 
    resultCollectin.Add(AddB(word)); 
}); 

// Do something with result 
+28

ParallelLoopResultはここでは何も役に立ちません。 +1でも – usr

+1

.AsParallel()でLINQの方がずっと良いでしょう –

+0

複数のスレッドからリストに追加するのはスレッドセーフですか? –

11
のように、 System.Collections.Concurrent下のコレクションの一つを使用することを検討してください

ConcurrentBagを使用して極端に遅い結果を収集しないでください。 代わりにローカルロックを使用してください。

どのようにこのような何かについて
var resultCollection = new List<string>(); 
object localLockObject = new object(); 

Parallel.ForEach<string, List<string>>(
     words, 
    () => { return new List<string>(); }, 
     (word, state, localList) => 
     { 
     localList.Add(AddB(word)); 
     return localList; 
     }, 
     (finalResult) => { lock (localLockObject) resultCollection.AddRange(finalResult); } 
); 

// Do something with resultCollection here 
+0

ConcurrentBagが独自のオブジェクトロックを使用するよりも遅いことを示す統計はありますか?オブジェクトのロックを使用するよりもコードの外観をきれいにするので、遅いことを知りたいだけです。 –

+0

@dineshygv IMHOの違いはごくわずかですhttp://stackoverflow.com/questions/2950955/concurrentbagof-mytype-vs-listof-mytype/34016915#34016915 –

+0

または全くロックを使用しません;-) – Steves

2

:私はあなたが和を計算または組み合わせたような結果が互いに(と対話する必要がある場合を除き、ロックまたは同時のオブジェクトが必要ではないと信じて

public class WordContainer 
{ 
    public WordContainer(string word) 
    { 
     Word = word; 
    } 

    public string Word { get; private set; } 
    public string Result { get; set; } 
} 

public class WordMaker 
{ 
    public void MakeIt() 
    { 
     string[] words = { "ack", "ook" }; 
     List<WordContainer> containers = words.Select(w => new WordContainer(w)).ToList(); 

     Parallel.ForEach(containers, AddB); 

     //containers.ForEach(c => Console.WriteLine(c.Result)); 
     foreach (var container in containers) 
     { 
      Console.WriteLine(container.Result); 
     } 

     Console.ReadKey(); 
    } 

    public void AddB(WordContainer container) 
    { 
     container.Result = "b" + container.Word; 
    } 
} 

すべての言葉)。この場合、ForEachは元のリストをきれいに分割し、各スレッドに独自のオブジェクトを渡して、他のスレッドとの干渉を心配することなく、必要なものすべてを操作できるようにします。

+0

はい、そうですコンソールアプリケーションでは動作しますが、コンソールアプリケーションではイベントをコレクション内で最初に集計してください。そうしないと、インターリーブ結果がコンソールウィンドウに表示されます。 – MatthewMartin

+0

Console.WriteLineコマンドはメインスレッドで同期して実行されており、Parallel.ForEachがすべてのリストアイテムの処理を終了して元のリストに定義された順序で結果を出力します。 Parallel.ForEach内からWriteLineを呼び出すと、結果はインターリーブされます。 – MichaC

11

AsParallel拡張メソッドIEnumerableを使用することを検討しても、それはあなたのために並行処理を行い、結果を収集します。

words.AsParallel().Select(AddB).ToArray()

同期(例えば、ロックまたはロックを使用する同時コレクション)は、通常、並行アルゴリズムのボトルネックです。最高の同期はできるだけ避けることです。私は、AsParallelが、単一のスレッドで生成されたすべてのアイテムをローカルの非並行コレクションに入れ、最後にこれらを結合するような、よりスマートなものを使用していると推測しています。

+1

これははるかに優れています。 –

1

これは、安全で速く、簡単なようだ:

public string[] MakeIt() { 
     string[] words = { "ack", "ook" }; 
     string[] results = new string[words.Length]; 
     ParallelLoopResult result = 
      Parallel.For(0, words.Length, i => results[i] = AddB(words[i])); 
     return results; 
    } 
+0

これは、キャッシュのピンポンを引き起こす可能性がありますが、コンカレント・コレクションよりも実質的に優れています。 – Steves

関連する問題