2016-12-08 11 views
6

흥미로운 문제가 발생했습니다. 변경되는 동안 ConcurrentDictionary<TKey, TValue>이 안전하게 열거 될 수 있다는 것을 알고 있고 (내 경우에는) 여러 번 사라질 수도있는 요소를 반복하는 부작용이 있으므로 ToList()을 사용하여 직접 스냅 샷을 생성하기로 결정했습니다. ConcurrentDictionary<TKey, TValue>ICollection<KeyValuePair<TKey, TValue>>을 구현하므로 List(IEnumerable<T> collection)이 사용되고 현재 항목 Count을 사용하여 사전의 현재 크기로 배열을 만든 다음 using ICollection<T>.CopyTo(T[] array, int arrayIndex) 항목을 복사하고 해당 ConcurrentDictionary<TKey, TValue> 구현을 호출하고 마지막으로 한편 요소가 사전에 추가되면 ArgumentException을 던집니다.항목 추가 중 ConcurrentDictionary <TKey, TValue>에 ToList() 호출

전체를 잠그면 컬렉션을 그대로 사용하는 것이 중단되므로 내 옵션이 예외를 잡아 계속 시도하고 (문제의 올바른 대답이 아님) 또는 내 구현을 다시 시도하는 것 같습니다. 이 문제에 대해 전문화 된 ToList()의 자체 버전 (그러나 다음으로 목록을 단순히 확장 한 다음 몇 가지 요소를 올바른 크기로 자르면 잔인한 것처럼 보이고 LinkedList를 사용하면 인덱싱 성능이 저하됨). 또한

, 성능의 비용으로 문제를 고쳐야 할 것 같다 않습니다 (예 : OrderBy 등) 백그라운드에서 버퍼의 일종을 만들 특정 LINQ 방법을 추가하는 것 같아,하지만 베어 ToList()는 분명하지 않으며, 추가 기능이 필요하지 않을 때 다른 방법으로 "기능을 추가"할 가치가 없습니다.

동시 수집시 문제가 될 수 있습니까?

그러한 스냅 샷을 생성하는 동안 성능 적중률을 최소로 유지하는 적절한 해결 방법은 무엇입니까? (바람직하게는 일부 LINQ 마법의 말에.)

이 편집 :

그것으로보고 후에 내가 확인할 수, ToArray() 정말 같은 스냅 샷의 문제를 해결하지 (난 그냥 어제 통과라고 생각합니다) 그저 단순한 스냅 샷일 뿐이므로 스냅 샷 (필터링, 정렬 등)을 수행하기 전에 추가 기능이 필요할 때 도움이되지 않으며 목록/배열이 여전히 필요합니다. (이 경우 새로운 콜렉션을 다시 작성해야하는 추가 호출이 필요합니다.)

스냅 샷이 이러한 수정을 거칠 수도 있고 그렇지 않을 수도 있음을 지적하지 못했기 때문에 끝, 바람직하게는, 그래서 나는 이것들을 질문들에 첨가 할 것이다.

(사람이 제목에 대한 더 나은 아이디어가있는 경우 또한, 말하지 않습니다.)

+2

대신 형식에 따라 특별히 구현 된'.ToArray()'를 사용하십시오. –

답변

6

이의이 답할 수 있도록 광범위한 이상 - 그림자 모든 동시 유형 여기 질문 :

당신이 분할하는 경우 , 모든 단계가 "동기화되어야"하는 여러 단계의 내부 작업을 처리하는 작업은 스레드 동기화로 인해 크래시와 이상한 결과를 얻게됩니다.

그래서 먼저 은 확실히 당신이 두 부분의 기회를 가질 것, 그 다음 크기 배열, .Count를 요청하고 목록의 값과 장소를 잡아 foreach를 사용 .ToList()를 사용하는 경우 다른 수의 요소를 얻습니다.

솔직히 말해서, 이러한 동시 유형은 많은 인터페이스를 구현하여 정상적인 콜렉션을 수행하려하지 않았 으면 좋겠지 만, 그렇습니다.

문제를 아는 즉시 코드를 수정할 수 있습니까?

그렇습니다. 유형 문서를 살펴보고 위에서 언급 한 문제가 발생하지 않는 스냅 샷 메커니즘의 형태를 제공하는지 확인해야합니다.

System.Collections.Concurrent.ConcurrentDictionary 복사 키 및 값 쌍의 스냅을 포함하는 새로운 배열 :

함께 documented.ToArray()이다 ConcurrentDictionary<TKey, TValue> 구현을 낸다.

(내 강조)

어떻게 .ToArray()는 현재 구현되어 ?

Using locks

, 당신은 스냅 샷을 얻기 위해 전체 사전을 잠금 생각한다면 그래서 시작하는 내용의 스냅 샷을 잡는 행위에 의문을 것이다 너무 비용이 많이 드는 라인을 697.

를 참조하십시오.

또한, .GetEnumerator() 방법은 documentation에서, 같은 규칙의 일부를 다음과 다음 열거 사전에서 반환

읽기 및 사전에 기록과 동시에를 사용하는 것이 안전 이다, 그러나 그것은 않습니다 사전의 순간 스냅 샷을 나타내지는 않습니다. 열거자를 통해 노출 된 내용은 GetEnumerator가이라고 지정된 후 사전에 수정 된 내용을 포함 할 수 있습니다.

(다시, 내 emhpasis)

그래서 .GetEnumerator()하지 충돌, 당신이 원하는 결과를 얻지 못할 수도 반면.

타이밍에 따라 다르지만 .ToArray() 일 수 있습니다. 따라서 모두 다릅니다.