다음을 통해 공유


PLINQ에서의 순서 보존

PLINQ에서 목표는 정확성을 유지하면서 성능을 최대화하는 것입니다. 쿼리는 가능한 한 빨리 실행되지만 여전히 올바른 결과를 생성해야 합니다. 경우에 따라 정확성을 유지하려면 원본 시퀀스의 순서를 유지해야 합니다. 그러나 순서 지정은 계산 비용이 많이 들 수 있습니다. 따라서 기본적으로 PLINQ는 원본 시퀀스의 순서를 유지하지 않습니다. 이와 관련하여 PLINQ는 LINQ to SQL과 유사하지만 순서를 유지하는 LINQ to Objects와는 다릅니다.

기본 동작을 재정의하려면 원본 시퀀스에서 AsOrdered 연산자를 사용하여 순서 유지를 설정할 수 있습니다. 그런 다음, 쿼리의 후반부에서 AsUnordered 메서드를 사용하여 순서 유지를 해제할 수 있습니다. 두 메서드를 모두 사용하면 쿼리를 병렬로 실행할지 순차적으로 실행할지 여부를 결정하는 추론에 따라 쿼리가 처리됩니다. 자세한 내용은 PLINQ의 속도 향상 이해를 참조하세요.

다음 예제에서는 어떤 방식으로든 결과를 정렬하지 않고 조건과 일치하는 모든 요소를 필터링하는 정렬되지 않은 병렬 쿼리를 보여 줍니다.

var cityQuery =
    (from city in cities.AsParallel()
     where city.Population > 10000
     select city).Take(1000);
Dim cityQuery = From city In cities.AsParallel()
                Where city.Population > 10000
                Take (1000)

이 쿼리는 조건을 충족하는 원본 시퀀스에서 처음 1000개 도시를 반드시 생성하는 것이 아니라 조건을 충족하는 1000개 도시 집합을 생성합니다. PLINQ 쿼리 연산자는 원본 시퀀스를 동시 작업으로 처리되는 여러 하위 시퀀스로 분할합니다. 순서 유지를 지정하지 않으면 각 파티션의 결과가 임의의 순서로 쿼리의 다음 단계로 전달됩니다. 또한 파티션은 나머지 요소를 계속 처리하기 전에 결과의 하위 집합을 생성할 수 있습니다. 결과 순서는 매번 다를 수 있습니다. 애플리케이션은 운영 체제가 스레드를 예약하는 방법에 따라 달라지므로 이를 제어할 수 없습니다.

다음 예제에서는 소스 시퀀스에서 연산자를 사용하여 AsOrdered 기본 동작을 재정의합니다. 이렇게 하면 이 메서드가 Take 조건을 충족하는 소스 시퀀스에서 처음 1000개의 도시를 반환합니다.

var orderedCities =
    (from city in cities.AsParallel().AsOrdered()
     where city.Population > 10000
     select city).Take(1000);

Dim orderedCities = From city In cities.AsParallel().AsOrdered()
                    Where city.Population > 10000
                    Take (1000)

그러나 이 쿼리는 파티션 전체에서 원래 순서를 추적하고 병합 시 순서가 일관되도록 해야 하기 때문에 순서가 지정되지 않은 버전만큼 빠르게 실행되지 않을 수 있습니다. 따라서 필요한 경우에만 사용하고 필요한 쿼리의 해당 부분에만 사용하는 AsOrdered 것이 좋습니다. 주문 보존이 더 이상 필요하지 않은 경우 이를 해제하는 데 사용합니다 AsUnordered . 다음 예제에서는 두 개의 쿼리를 작성하여 이를 수행합니다.

var orderedCities2 =
    (from city in cities.AsParallel().AsOrdered()
     where city.Population > 10000
     select city).Take(1000);

var finalResult =
    from city in orderedCities2.AsUnordered()
    join p in people.AsParallel()
    on city.Name equals p.CityName into details
    from c in details
    select new
    {
        city.Name,
        Pop = city.Population,
        c.Mayor
    };

foreach (var city in finalResult) { /*...*/ }
Dim orderedCities2 = From city In cities.AsParallel().AsOrdered()
                     Where city.Population > 10000
                     Select city
                     Take (1000)

Dim finalResult = From city In orderedCities2.AsUnordered()
                  Join p In people.AsParallel() On city.Name Equals p.CityName
                  Select New With {.Name = city.Name, .Pop = city.Population, .Mayor = city.Mayor}

For Each city In finalResult
    Console.WriteLine(city.Name & ":" & city.Pop & ":" & city.Mayor)
Next

PLINQ는 쿼리의 나머지 부분에 대해 순서를 지정하는 연산자가 생성한 시퀀스의 순서를 유지합니다. 즉, OrderByThenBy와 같은 연산자는 마치 AsOrdered가 호출된 것처럼 처리됩니다.

쿼리 연산자 및 순서 지정

다음 쿼리 연산자는 쿼리의 모든 후속 작업 또는 호출될 때까지 AsUnordered 순서 유지를 도입합니다.

다음 PLINQ 쿼리 연산자는 경우에 따라 올바른 결과를 생성하기 위해 순서가 지정된 소스 시퀀스가 필요할 수 있습니다.

일부 PLINQ 쿼리 연산자는 원본 시퀀스의 순서가 정렬되었는지 순서가 지정되지 않았는지에 따라 다르게 동작합니다. 다음 표에서는 이러한 연산자를 나열합니다.

오퍼레이터 원본 시퀀스의 순서가 지정된 경우의 결과 소스 시퀀스의 순서가 지정되지 않은 경우의 결과
Aggregate 비연결 또는 비커밋 작업에 대한 비결정적 출력 비연결 또는 비커밋 작업에 대한 비결정적 출력
All 해당 없음 해당 없음
Any 해당 없음 해당 없음
AsEnumerable 해당 없음 해당 없음
Average 비연결 또는 비커밋 작업에 대한 비결정적 출력 비연결 또는 비커밋 작업에 대한 비결정적 출력
Cast 정렬된 결과 순서가 지정되지 않은 결과
Concat 정렬된 결과 순서가 지정되지 않은 결과
Count 해당 없음 해당 없음
DefaultIfEmpty 해당 없음 해당 없음
Distinct 정렬된 결과 순서가 지정되지 않은 결과
ElementAt 지정된 요소 반환 임의 요소
ElementAtOrDefault 지정된 요소 반환 임의 요소
Except 순서가 지정되지 않은 결과 순서가 지정되지 않은 결과
First 지정된 요소 반환 임의 요소
FirstOrDefault 지정된 요소 반환 임의 요소
ForAll 비결정적으로 병렬로 실행 비결정적으로 병렬로 실행
GroupBy 정렬된 결과 순서가 지정되지 않은 결과
GroupJoin 정렬된 결과 순서가 지정되지 않은 결과
Intersect 정렬된 결과 순서가 지정되지 않은 결과
Join 정렬된 결과 순서가 지정되지 않은 결과
Last 지정된 요소 반환 임의 요소
LastOrDefault 지정된 요소 반환 임의 요소
LongCount 해당 없음 해당 없음
Min 해당 없음 해당 없음
OrderBy 시퀀스 다시 정렬 새 순서가 지정된 섹션을 시작합니다.
OrderByDescending 시퀀스 다시 정렬 새 순서가 지정된 섹션을 시작합니다.
Range 해당 없음(기본값과 동일 AsParallel ) 해당 없음
Repeat 해당 없음(기본값과 동일 AsParallel) 해당 없음
Reverse 역전하다 아무 작업도 하지 않음
Select 정렬된 결과 순서가 지정되지 않은 결과
Select (인덱싱됨) 정렬된 결과 순서가 지정되지 않은 결과입니다.
SelectMany 정렬된 결과입니다. 순서가 지정되지 않은 결과
SelectMany (인덱싱됨) 정렬된 결과입니다. 순서가 지정되지 않은 결과입니다.
SequenceEqual 순서가 지정된 비교 순서가 지정되지 않은 비교
Single 해당 없음 해당 없음
SingleOrDefault 해당 없음 해당 없음
Skip 첫 번째 n개 요소를 건너뜁니다. n 요소를 건너뜁니다
SkipWhile 정렬된 결과입니다. 비결정적. 현재 임의 순서로 SkipWhile을 수행합니다.
Sum 비연결 또는 비커밋 작업에 대한 비결정적 출력 비연결 또는 비커밋 작업에 대한 비결정적 출력
Take 첫 번째 n 요소를 사용합니다. 어떤 n 요소를 가져옵니다.
TakeWhile 정렬된 결과 비결정적. 현재 임의 순서로 TakeWhile 수행
ThenBy 보충제 OrderBy 보충제 OrderBy
ThenByDescending 보충제 OrderBy 보충제 OrderBy
ToArray 정렬된 결과 순서가 지정되지 않은 결과
ToDictionary 해당 없음 해당 없음
ToList 정렬된 결과 순서가 지정되지 않은 결과
ToLookup 정렬된 결과 순서가 지정되지 않은 결과
Union 정렬된 결과 순서가 지정되지 않은 결과
Where 정렬된 결과 순서가 지정되지 않은 결과
Where (인덱싱됨) 정렬된 결과 순서가 지정되지 않은 결과
Zip 정렬된 결과 순서가 지정되지 않은 결과

순서가 지정되지 않은 결과는 적극적으로 순서를 섞지 않습니다. 그들은 단순히 그들에게 적용 된 특별한 순서 논리가 없습니다. 경우에 따라 순서가 지정되지 않은 쿼리는 원본 시퀀스의 순서를 유지할 수 있습니다. 인덱싱된 Select 연산자를 사용하는 쿼리의 경우 PLINQ는 출력 요소가 인덱스 증가 순서대로 나오도록 보장하지만 어떤 인덱스가 어떤 요소에 할당될지 보장하지 않습니다.

참고하십시오