68

遅い Web サービスを並行して呼び出しています。サービスから情報を取得する必要があることに気付くまで、物事は素晴らしかった. しかし、値を戻す場所がわかりません。データベースに書き込めません。Parallel.ForEach を使用して呼び出されたメソッド内で HttpContext.Current が null のようです。

以下にサンプル プログラムを示します (文字列連結ではなく、遅い Web サービスを想像してください)。

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;
        }
    }

}
4

6 に答える 6

75

あなたはそれをここで捨てました。

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

おそらく、次のようなものが必要です。

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

この最後にある種のコレクションが必要な場合は、以下のコレクションのいずれかを使用することを検討してくださいSystem.Collections.ConcurrentConcurrentBag

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

// Do something with the result
于 2012-09-26T21:42:58.187 に答える
37

AsParallelの拡張メソッドを使用することを検討しIEnumerableてください。同時実行を処理し、結果を収集します。

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

同期 (例: ロックまたはロックを使用する並行コレクション) は通常、並行アルゴリズムのボトルネックです。最善の方法は、できるだけ同期を避けることです。AsParallelシングルスレッドで生成されたすべてのアイテムをローカルの非並行コレクションに入れて、最後にこれらを組み合わせるなど、よりスマートなものを使用していると思います。

于 2016-09-28T08:11:01.120 に答える
14

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
于 2014-11-25T13:21:33.760 に答える
5

これは安全で、高速で、簡単に思えます。

    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;
    }
于 2017-01-20T15:20:54.267 に答える
3

このようなものはどうですか:

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 は元のリストをきちんと分割し、他のスレッドとの干渉を心配することなく必要なすべてを操作できる独自のオブジェクトを各スレッドに渡します。

于 2015-04-02T23:36:41.973 に答える