.NET Framework 4에는 다중 스레드 추가 및 제거 작업을 지원하도록 특별히 설계된 5가지 컬렉션 형식이 도입되었습니다. 스레드 보안을 달성하기 위해 이러한 형식은 다양한 종류의 효율적인 잠금 및 잠금 없는 동기화 메커니즘을 사용합니다. 동기화는 작업에 오버헤드를 추가합니다. 오버헤드의 양은 사용되는 동기화의 종류, 수행되는 작업의 종류 및 컬렉션에 동시에 액세스하려는 스레드 수와 같은 기타 요인에 따라 달라집니다.
일부 시나리오에서는 동기화 오버헤드가 무시할 수 있을 정도로 적어 외부 잠금으로 보호될 때, 다중 스레드 형식이 스레드 안전이 보장되지 않는 형식보다 훨씬 빠르고 성능 확장이 훨씬 더 뛰어납니다. 다른 시나리오에서는 오버헤드로 인해 스레드 안전 형식의 성능 및 확장성이 외부 잠금 방식의 스레드 비안전 형식 버전과 거의 동일하거나 더 느려질 수 있습니다.
다음 섹션에서는 스레드로부터 안전한 컬렉션을 사용하는 시기와 읽기 및 쓰기 작업 주위에 사용자가 제공한 잠금이 있는 스레드로부터 안전하지 않은 컬렉션을 사용하는 경우에 대한 일반적인 지침을 제공합니다. 성능은 여러 요인에 따라 달라질 수 있으므로 지침은 구체적이지 않으며 모든 상황에서 반드시 유효하지는 않습니다. 성능이 매우 중요한 경우 사용할 컬렉션 유형을 결정하는 가장 좋은 방법은 대표적인 컴퓨터 구성 및 로드에 따라 성능을 측정하는 것입니다. 이 문서에서는 다음 용어를 사용합니다.
순수 생산자-소비자 시나리오
지정된 스레드는 요소를 추가하거나 제거하지만 둘 다 제거하지는 않습니다.
혼합 생산자-소비자 시나리오
지정된 스레드는 요소를 추가 및 제거하는 것입니다.
속도 향상
동일한 시나리오에서 다른 형식에 비해 알고리즘 성능이 빨라집니다.
확장성
컴퓨터의 코어 수에 비례하는 성능 향상. 크기 조정 알고리즘은 8개 코어에서 두 코어보다 더 빠르게 수행됩니다.
ConcurrentQueue(T) 대 Queue(T)
각 요소에 대한 처리 시간이 매우 작은 순수 생산자 소비자 시나리오(몇 가지 지침) System.Collections.Concurrent.ConcurrentQueue<T> 에서는 외부 잠금이 있는 시나리오에 비해 System.Collections.Generic.Queue<T> 적절한 성능 이점을 제공할 수 있습니다. 이 시나리오 ConcurrentQueue<T> 에서는 하나의 전용 스레드가 큐에 대기 중이고 하나의 전용 스레드가 큐를 해제할 때 가장 잘 수행됩니다. 이 규칙을 Queue<T> 적용하지 않으면 코어가 여러 개 있는 컴퓨터보다 ConcurrentQueue<T> 약간 더 빠르게 수행할 수도 있습니다.
처리 시간이 약 500 FLOPS(부동 소수점 작업) 이상인 경우 두 스레드 규칙이 적용되지 ConcurrentQueue<T>않으므로 확장성이 매우 좋습니다. Queue<T> 는 이 시나리오에서 잘 확장되지 않습니다.
혼합 생산자-소비자 시나리오에서 처리 시간이 매우 짧을 때, 외부 잠금이 있는 Queue<T>이 ConcurrentQueue<T>보다 더 잘 확장됩니다. 그러나 처리 시간이 약 500 FLOPS 이상인 경우 ConcurrentQueue<T>가 더 잘 확장됩니다.
ConcurrentStack 대 Stack
순수 생산자-소비자 시나리오에서 처리 시간이 매우 짧을 때 System.Collections.Concurrent.ConcurrentStack<T>와 System.Collections.Generic.Stack<T>가 외부 잠금을 공유하는 경우, 전용 푸시 스레드 1개와 전용 팝 스레드 1개가 거의 동일한 성능을 발휘할 가능성이 높습니다. 그러나 스레드 수가 증가함에 따라 두 형식 모두 경합 증가로 인해 속도가 느려지고 Stack<T> 성능이 더 ConcurrentStack<T>좋을 수 있습니다. 처리 시간이 약 500 FLOPS 이상인 경우 두 형식 모두 거의 동일한 속도로 크기 조정됩니다.
혼합 생산자-소비자 시나리오 ConcurrentStack<T> 에서는 소규모 및 대규모 워크로드 모두에 대해 더 빠릅니다.
PushRange TryPopRange 사용은 액세스 시간을 크게 단축할 수 있습니다.
ConcurrentDictionary 대 Dictionary
일반적으로 여러 스레드에서 키 또는 값을 동시에 추가하고 업데이트하는 시나리오에서 사용합니다 System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue> . 자주 업데이트되고 읽기가 상대적으로 적은 ConcurrentDictionary<TKey,TValue> 시나리오에서는 일반적으로 적당한 이점을 제공합니다. 읽기 및 업데이트가 많은 시나리오에서는 ConcurrentDictionary<TKey,TValue> 가 포함된 경우, 코어가 있는 컴퓨터에서 일반적으로 훨씬 더 빠릅니다.
자주 업데이트되는 시나리오에서는 동시성 수준을 높인 다음 더 많은 코어가 있는 컴퓨터에서 ConcurrentDictionary<TKey,TValue> 성능이 향상되는지 여부를 측정할 수 있습니다. 동시성 수준을 변경하는 경우 전역 작업을 최대한 피합니다.
키 또는 값만 읽는 경우, 사전이 스레드에서 수정되지 않는다면 동기화가 필요하지 않기 때문에 Dictionary<TKey,TValue>의 속도가 더 빠릅니다.
ConcurrentBag (컨커런트백)
순수 생산자-소비자 시나리오 System.Collections.Concurrent.ConcurrentBag<T> 에서는 다른 동시 컬렉션 형식보다 더 느리게 수행될 수 있습니다.
혼합 생산자-소비자 시나리오 ConcurrentBag<T> 에서는 일반적으로 크고 작은 워크로드에 대한 다른 동시 컬렉션 유형보다 훨씬 빠르고 확장성이 높습니다.
BlockingCollection
경계 및 차단 의미 체계가 필요한 System.Collections.Concurrent.BlockingCollection<T> 경우 사용자 지정 구현보다 더 빠르게 수행될 수 있습니다. 또한 다양한 취소, 열거형 및 예외 처리를 지원합니다.
참고하십시오
.NET