목록에 추가하는 Parallel.ForEach


80

네트워크를 통해 원격 사이트에 연결하고 일반 목록을 반환하는 여러 기능을 실행하려고합니다. 하지만 동시에 실행하고 싶습니다.

예를 들면 :

public static List<SearchResult> Search(string title)
{
    //Initialize a new temp list to hold all search results
    List<SearchResult> results = new List<SearchResult>();

    //Loop all providers simultaneously
    Parallel.ForEach(Providers, currentProvider =>
    {
        List<SearchResult> tmpResults = currentProvider.SearchTitle((title));

        //Add results from current provider
        results.AddRange(tmpResults);
    });

    //Return all combined results
    return results;
}

내가보기에 '결과'에 대한 여러 삽입이 동시에 발생할 수 있습니다. 이로 인해 애플리케이션이 중단 될 수 있습니다.

이것을 어떻게 피할 수 있습니까?


어떤 .NET 버전을 사용하고 있습니까?
sll

4
.Net 4 이상이어야합니다. 거기에 병렬이 도입되었습니다.
Matt Mills

답변:


58
//In the class scope:
Object lockMe = new Object();    

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}

기본적으로 잠금은 하나의 스레드 만 해당 중요 섹션에 동시에 액세스 할 수 있음을 의미합니다.


1
그러나 해당 결과가 추가되는 동안 다른 공급자의 결과를 추가하려고하면 어떻게됩니까? 가능할 때까지 실패하거나 대기합니까?
shaharmor

4
잠금이있을 때 스레드는 잠금을 얻을 수있을 때까지 대기합니다.
Haedrian 2011

따라서 기본적으로 다음과 같은 말이 있습니다.
shaharmor

8
사소한 점 : this잠금 개체에 대한 가장 안전한 선택이 아닙니다. 특별한 개인 개체를 사용하는 것이 더 좋습니다 : lock(resultsLock).
Henk Holterman 2011

2
locks... 동시 컬렉션을 피하기 위해 더 나은 것 같다하지만 전체 실행 시간을 늦출 수
피오트르 쿨라

155

동시 컬렉션을 사용할 수 있습니다 .

System.Collections.Concurrent네임 스페이스에서 해당 유형의 장소에서 사용되어야합니다 여러 스레드 안전 컬렉션 클래스 제공 System.CollectionsSystem.Collections.Generic복수의 thread가 동시에 컬렉션에 액세스 할 때마다 네임 스페이스를.

예를 들어 ConcurrentBag항목이 추가 될 순서를 보장 할 수 없기 때문에 사용할 수 있습니다.

스레드로부터 안전하고 순서가 지정되지 않은 개체 컬렉션을 나타냅니다.


예, 이것이 실제 답입니다. 동시 컬렉션을 사용하면 일반적으로 더 나은 성능을 얻을 수 있습니다.
lkg

32

코드를 선호하는 사람들을 위해 :

public static ConcurrentBag<SearchResult> Search(string title)
{
    var results = new ConcurrentBag<SearchResult>();
    Parallel.ForEach(Providers, currentProvider =>
    {
        results.Add(currentProvider.SearchTitle((title)));
    });

    return results;
}

: 루프 사용해야합니다 foreach (var item in currentProvider.SearchTitle((title))) results.Add(item);
안토니 맥그래스

25

동시 컬렉션은 .Net 4의 새로운 기능입니다. 새로운 병렬 기능과 함께 작동하도록 설계되었습니다.

.NET Framework 4의 동시 컬렉션을 참조하세요 .

.NET 4 이전에는 여러 스레드가 단일 공유 컬렉션에 액세스 할 수있는 경우 고유 한 동기화 메커니즘을 제공해야했습니다. 컬렉션을 잠 가야했습니다 ...

... System.Collections.Concurrent의 [새로운] 클래스와 인터페이스 [.NET 4에 추가됨]는 스레드 간의 공유 데이터와 관련된 [...] 다중 스레드 프로그래밍 문제에 대한 일관된 구현을 제공합니다.


14

이것은 PLINQ AsParallelSelectMany다음을 사용하여 간결하게 표현할 수 있습니다 .

public static List<SearchResult> Search(string title)
{
    return Providers.AsParallel()
                    .SelectMany(p => p.SearchTitle(title))
                    .ToList();
}

linq selectMany는 훌륭하지만 슬프게도 linq는 일반적인 foreach보다 느립니다. :(
Kugan Kumar

6
마이크로 최적화하지 마십시오. OP SearchTitle는 원격 사이트에 연결하는 것을 암시합니다 . 지연 시간은 LINQ와 foreach의 차이보다 몇 배 더 느립니다.
Douglas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.