2009-12-22 9 views
99

ConcurrentDictionary 유형에 대한 충분한 정보를 찾을 수 없으므로 여기에서 질문했습니다..NET - Dictionary locking vs. ConcurrentDictionary

현재는 Dictionary을 사용하여 스레드 풀의 정확한 스레드 양이 아닌 여러 스레드에 의해 지속적으로 액세스되는 모든 사용자를 보유하고 액세스를 동기화했습니다.

저는 최근에 .NET 4.0에 스레드로부터 안전한 컬렉션 집합이 있다는 것을 알았습니다. 매우 기쁜 것 같습니다. 궁금한 점은 동기화 된 액세스로 일반 Dictionary을 사용하거나 또는 스레드 안전성이있는 ConcurrentDictionary을 사용하는 것 중에서 선택할 수있는 옵션이있어 '더 효율적이고 관리하기 쉽습니다.'옵션이 무엇인지 궁금합니다.

Reference to .NET 4.0's ConcurrentDictionary

+2

당신은 (HTTP [주제에이 코드 프로젝트 기사]를 참조 할 수 있습니다 : //www.codeproject를.com/Articles/548406/Dictionary-plus-Locking-versus-ConcurrentDictionar) Concurrent Dictionery를 진행하면 – nawfal

답변

124

스레드 세이프 컬렉션 대 비 threadsafe 컬렉션은 다른 방식으로 볼 수 있습니다.

계산대를 제외하고 점원이없는 점을 고려하십시오. 사람들이 책임감있게 행동하지 않으면 많은 문제가 있습니다. 예를 들어, 고객이 현재 피라미드를 만들면서 피라미드 캔에서 캔을 가져 간다고 가정 해 보겠습니다. 모든 지옥은 느슨해집니다. 또는 동시에 두 명의 고객이 같은 항목에 도달하면 누가 이깁니 까? 싸울 것인가? 이것은 threadsafe가 아닌 콜렉션입니다. 문제를 피할 수있는 방법은 많지만 모든 종류의 잠금 또는 어떤 방식 으로든 명시 적으로 액세스해야합니다.

한편 책상에 점원이있는 점포를 고려해 볼 때 상점을 통해서만 쇼핑을 할 수 있습니다. 너는 일렬로 나란히 서서, 그에게 물건을 요구한다, 그는 그것을 당신에게 되 가져온다. 그리고 당신은 선에서 나온다. 여러 품목이 필요하다면 기억할 수있는만큼의 왕복 여행 당 많은 품목을 픽업 할 수 있지만 점원을 호되게 피하지 않도록주의해야합니다. 그러면 다른 고객이 뒤에서 분노하게됩니다.

이제 이것을 고려하십시오. 점원이 한 명있는 가게에서 선의 맨 끝까지 가면 점원에게 "화장지가 있습니까?"라고 묻습니다. 그리고 그는 "예"라고 말한 다음 " 내가 얼마나 필요한지 알게되면 다시 돌아올거야. "라고 말하면서, 당신이 줄 서기 전까지 상점은 매진 될 수 있습니다. 이 시나리오는 스레드 세이프 콜렉션에 의해 방지되지 않습니다.

threadsafe 수집은 다중 스레드에서 액세스 된 경우에도 내부 데이터 구조가 항상 유효 함을 보장합니다.

비 threadsafe 컬렉션에는 이러한 보증이 제공되지 않습니다. 예를 들어, 한 스레드에서 이진 트리에 무언가를 추가하는 경우 다른 스레드가 트리의 균형을 조정하는 동안 바쁜 경우 아이템이 추가되거나 트리가 여전히 유효하다는 보장이 없기 때문에 희망을 넘어 부패 할 수 있습니다.

스레드 세이프 수집은, 그러나,에 스레드에서 모든 작업을하는 순차적 인 작업을 보장하지 않습니다 같은 의미 내부 데이터 구조의 "스냅 샷"당신은 같은 코드가있는 경우 :

if (tree.Count > 0) 
    Debug.WriteLine(tree.First().ToString()); 

tree.Counttree.First() 사이에 다른 스레드가 트리의 나머지 노드를 지우므로 First()null을 반환하기 때문에 NullReferenceException이 발생할 수 있습니다.

이 시나리오에서는 원하는 컬렉션을 얻는 데 안전한 방법이 있는지 확인해야합니다. 위의 코드를 다시 작성해야하거나 잠글 필요가 있습니다.

+7

이 글을 읽을 때마다, 비록 그것이 사실 일지라도, 어쨌든 우리는 잘못된 길을 가고 있습니다. –

+18

스레드 기반 응용 프로그램의 주된 문제점은 변경 가능한 데이터 구조입니다. 이를 피할 수 있다면 문제를 스스로 해결할 수 있습니다. –

+0

분명히 사전에는 정말 불쾌한 스레딩 부작용이있을 수 있습니다. http://stackoverflow.com/questions/14838032/asp-net-hang-generic-dictionary-concurrency-issues-causes-gc-deadlock – jocull

5

은 기본적으로 새 ConcurrentDictionary로 가고 싶어. 상자에서 꺼내기 만하면 스레드로부터 안전한 프로그램을 만들기 위해 적은 코드를 작성해야합니다.

+0

에 문제가 있습니까? – kbvishnu

53

thread-safe 컬렉션을 사용할 때 여전히주의해야합니다. thread-safe가 모든 스레딩 문제를 무시할 수 있다는 것을 의미하지는 않기 때문입니다. 컬렉션이 스레드 안전하다고 자신을 알리는 경우 일반적으로 여러 스레드가 동시에 읽고 쓰는 경우에도 일관된 상태를 유지합니다. 그러나 이것이 여러 메소드를 호출하는 경우 단일 스레드가 결과의 "논리적"순서를 볼 수 있음을 의미하지는 않습니다.

예를 들어, 키가 있는지 처음에 확인한 다음 나중에 키에 해당하는 값을 얻으면 다른 스레드가 키를 제거 할 수 있으므로 ConcurrentDictionary 버전에서도 해당 키가 더 이상 존재하지 않을 수 있습니다. 이 경우 잠금을 사용해야합니다 (또는 더 나은 방법은 TryGetValue을 사용하여 두 통화를 결합하는 것입니다).

그래서 사용하되 모든 동시성 문제를 무시할 수있는 무료 패스를 제공한다고 생각하지 마십시오. 여전히 조심해야합니다.

+4

기본적으로 객체를 올바르게 사용하지 않으면 작동하지 않는다고 말하는 것입니까? – ChaosPandion

+0

왜 그런가요? 그 컬렉션은 어떻게 든 잃어 버렸어? –

+1

기본적으로 Trysd는 일반적인 Contains -> Add 대신 사용해야합니다. – ChaosPandion

12

ConcurrentDictionary.GetOrAdd 메서드는 대부분의 멀티 스레드 시나리오에서 필요한 것입니다.

+1

TryGet도 사용합니다. 그렇지 않으면 키가 이미 존재할 때마다 두 번째 매개 변수를 GetOrAdd로 초기화하는 데 낭비하게됩니다. –

+5

* GetOrAdd *는 오버로드 * GetOrAdd (TKey, Func ) *를 가지므로 두 번째 매개 변수는 키가 사전에없는 경우에만 지연 초기화 될 수 있습니다. * TryGetValue *는 수정하지 않고 데이터를 가져 오는 데 사용됩니다. 이러한 각각의 메서드를 연속적으로 호출하면 다른 스레드가 두 호출 사이에 키를 추가하거나 제거 할 수 있습니다. – sgnsajgon

+0

Lasse의 게시물이 우수하더라도이 질문에 대한 답변이 명확해야합니다. – Spivonious

13

당신은 닷넷 3.5sp1에 대한 Reactive Extensions을 보았습니까? Jon Skeet에 따르면 그들은 .Net3.5 sp1에 대한 병렬 확장과 동시 데이터 구조의 번들을 백 포트했습니다.

.Net 4 Beta 2 용 샘플 세트가 있는데, 병렬 확장을 사용하는 방법에 대한 자세한 설명이 나와 있습니다.

I/O를 수행하기 위해 32 스레드를 사용하여 ConcurrentDictionary를 테스트하는 데 지난 주를 보냈습니다. 엄청난 양의 테스트가 실행되었음을 나타내는 광고 된 것처럼 작동하는 것 같습니다.

: .NET 4 ConcurrentDictionary 및 패턴.

Microsoft는 Paralell Programming의 Patterns라는 pdf 파일을 발표했습니다. 그것의 reallly 다운로드 가치가 정말 좋은 세부 사항에 적합한 패턴을 사용합니다. 닷넷 4 동시 확장 및 피하기 위해 안티 패턴. Here it is.

27

내부적으로 ConcurrentDictionary는 각 해시 버킷에 별도의 잠금을 사용합니다. 단일 항목에서 작동하는 Add/TryGetValue 및 이와 유사한 메소드 만 사용하는 경우, 사전은 각각의 간단한 성능 이점을 갖는 거의 잠금이없는 데이터 구조로 작동합니다. OTOH 열거 메소드 (Count 속성 포함)는 모든 버킷을 동시에 잠그므로 동기화 된 사전보다 성능이 현저히 떨어집니다.

나는 ConcurrentDictionary를 사용한다.

2

우리는 캐시 된 컬렉션을 위해 ConcurrentDictionary를 사용했습니다.이 컬렉션은 1 시간마다 다시 채워지고 Is this example thread safe? 질문과 비슷한 to the solution과 같이 여러 클라이언트 스레드에서 읽습니다.

ReadOnlyDictionary으로 변경하면 전반적인 성능이 향상됩니다.

+2

downvote의 이유를 알아 주셔서 감사합니다. –

+0

ReadOnlyDictionary는 다른 IDictionary를 둘러싼 래퍼입니다. 후드 아래에서 당신은 무엇을 사용합니까? – Lu55