programing

Parallel.ForEach에서 반환 값을 어떻게 수집합니까?

nicescript 2021. 1. 14. 08:05
반응형

Parallel.ForEach에서 반환 값을 어떻게 수집합니까?


느린 웹 서비스를 병렬로 호출하고 있습니다. 서비스에서 정보를 되 찾아야한다는 것을 깨달을 때까지 모든 것이 훌륭했습니다. 그러나 나는 가치를 되 찾을 곳을 보지 못합니다. 데이터베이스에 쓸 수 없습니다. HttpContext.Current는 Parallel.ForEach를 사용하여 호출 된 메서드 내부에서 null 인 것으로 보입니다.

아래는 샘플 프로그램입니다 (문자열 연결 대신 느린 웹 서비스를 상상해보십시오)

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

}

여기에서 버렸습니다.

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

아마도 다음과 같은 것을 원할 것입니다.

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

당신이이 말에 수집의 일종을 원하는 경우 아래의 컬렉션 중 하나를 사용하여 고려 System.Collections.Concurrent, 같은ConcurrentBag

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

// Do something with the result

AsParallel확장 방법을 사용하는 것을 고려할 수 IEnumerable있으며 동시성을 처리하고 결과를 수집합니다.

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

동기화 (예 : 잠금 또는 잠금을 사용하는 동시 수집)는 일반적으로 동시 알고리즘의 병목 현상입니다. 가장 좋은 방법은 가능한 한 동기화를 피하는 것입니다. AsParallel단일 스레드에서 생성 된 모든 항목을 로컬 비 동시 컬렉션에 넣은 다음 마지막에 결합하는 것과 같은 더 똑똑한 것을 사용한다고 생각합니다 .


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

다음과 같이 어떻습니까?

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

I believe the locking or concurrent objects isn't necessary unless you need the results to interact with one another (like you were computing a sum or combining all the words). In this case ForEach neatly breaks your original list up and hands each thread its own object that it can manipulate all it wants without worrying about interfering with the other threads.


This seems safe, fast, and simple:

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

ReferenceURL : https://stackoverflow.com/questions/12610868/how-do-i-collect-return-values-from-parallel-foreach

반응형