문제

을 찾을 수 없는 충분한 정보에 ConcurrentDictionary 유형,그래서 나는 그것에 대해 물어보십시오 여기입니다.

현재,사용 Dictionary 모든 사용자가 액세스하는 끊임없이 여러 스레드(스레드 풀에서,그래서 정확한 금액의 스레드),그리고 동기화된 액세스입니다.

나는 최근에 있다는 것을 발견했다는 설정의 스레드를 안전 컬렉션에서.NET4.0 며,그것은 매우 즐겁다.제가 궁금했던 것이 무엇이 될 수 있는 더 효율적이고 관리하기 쉽'옵션으로 내가 할 수있는 옵션이 정상적인 Dictionary 기기에 액세스하거나, ConcurrentDictionary 는 이미 스레드에 안전합니다.

참조.NET4.0 의 ConcurrentDictionary

도움이 되었습니까?

해결책

스레드-안전 컬렉션 대 비 스레드 안전 수집은 다른 방식으로 볼 수 있습니다.

결제를 제외하고 서기가없는 상점을 고려하십시오. 사람들이 책임감있게 행동하지 않으면 많은 문제가 있습니다. 예를 들어, 고객이 피라미드 캔에서 캔을 가져 가면서 직원이 현재 피라미드를 건설하는 동안 모든 지옥이 풀릴 것이라고 가정 해 봅시다. 또는 두 고객이 동시에 같은 품목에 도달하면 누가 이기면 어떨까요? 싸움이 있을까요? 이것은 비 스레드 스페인 수집입니다. 문제를 피하는 방법에는 여러 가지가 있지만 모두 어떤 종류의 잠금 장치 또는 다른 방식으로 명백한 접근이 필요합니다.

반면에, 책상에 서기가있는 가게를 고려하면 그를 통해서만 쇼핑 할 수 있습니다. 당신은 줄을 서서 그에게 아이템을 요청하고, 그는 당신에게 그것을 다시 가져오고, 당신은 줄에서 나갑니다. 여러 항목이 필요한 경우, 기억할 수있는만큼 각 왕복에서 많은 품목 만 픽업 할 수 있지만, 서기를 어지럽히는 것을 피하기 위해주의해야합니다. 이는 다른 고객이 뒤에 줄을 서서 분노 할 것입니다.

이제 이것을 고려하십시오. 한 점원이있는 가게에서, 당신이 줄의 앞쪽으로 가서 서기에게 "화장지가 있습니까?"라고 물어 보면 그는 "예"라고 말한 다음 "좋아요, i"라고 말합니다. 내가 얼마나 필요한지 알 때 다시 연락을 취하십시오. 이 시나리오는 ThreadSafe 컬렉션에 의해 방지되지 않습니다.

ThreadSafe Collection은 여러 스레드에서 액세스하더라도 내부 데이터 구조가 항상 유효합니다.

비 스레드 스페이프 컬렉션에는 그러한 보증이 제공되지 않습니다. 예를 들어, 한 스레드의 바이너리 트리에 무언가를 추가하고 다른 스레드가 트리를 재조정하는 바쁘지만 항목이 추가 될 것이라는 보장이 없거나 나중에 나무가 여전히 유효하다는 것을 보장하지 않으면 희망을 넘어서 손상 될 수 있습니다.

그러나 ThreadSafe Collection은 스레드의 순차적 작업이 모두 내부 데이터 구조의 동일한 "스냅 샷"에서 작동한다는 것을 보장하지는 않습니다. 즉, 코드가있는 경우 다음과 같은 코드가 있습니다.

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

중간에 NullReferenceException을 얻을 수 있습니다 tree.Count 그리고 tree.First(), 다른 스레드는 트리의 나머지 노드를 제거했습니다. First() 돌아올 것입니다 null.

이 시나리오의 경우 해당 컬렉션에 원하는 것을 얻을 수있는 안전한 방법이 있는지 확인해야합니다. 위의 코드를 다시 작성해야하거나 잠금해야 할 수도 있습니다.

다른 팁

당신은 여전히 매우 신중해야 할 때 사용하는 스레드에 안전 컬렉션하기 때문에 스레드에 안전하지 않는 것을 의미를 무시할 수 있습니다 모든 실을 꿰는 문제입니다.때 컬렉션을 자체로 thread-safe,그것은 일반적으로 의미하는 일관된 상태로 유지할 때에도 여러 스레드 읽기 및 쓰기를 동시에.하지만 그는 것을 의미하지 않는 하나의 스레드 표시됩"논리적"시퀀스의 결과는 경우 그것은 여러 방법이 있습니다.

예를 들어,만약 당신이 먼저 확인하는 경우에는 키가 있는 그리고 나중에는 값에 해당하는 키에는 키가 더 이상 존재하지 않을 수 있습니다 심지어 ConcurrentDictionary 버전(기 때문에 다른 스레드를 제거 할 수 있었 key).당신은 아직도를 사용해야에 잠금 이 경우(또는 더 나은:두가지를 사용하여 전화 TryGetValue).

그래서 그들을 사용 할,하지만 생각하지 않는 무료 전달을 모두 무시하는 동시성 문제입니다.당신은 여전히주의해야합니다.

내부적으로 동시 디디컬레이션은 각 해시 버킷마다 별도의 잠금을 사용합니다. Add/TrygetValue 및 단일 항목에서 작동하는 유사 메소드 만 사용하는 한 사전은 각각의 달콤한 성능 혜택을 가진 거의 잠금없는 데이터 구조로 작동합니다. OTOH 열거 방법 (카운트 속성 포함)을 한 번에 모든 버킷을 잠그므로 성능 면적으로 동기화 된 사전보다 나쁩니다.

나는 단지 ConcurrentDictionary를 사용한다고 말하고 싶습니다.

ConcurrentDictionary.getorAdd 메소드는 가장 멀티 스레드 시나리오가 필요로하는 것 같아요.

당신은 봤어 반응성 확장 .NET 3.5SP1 용. Jon Skeet에 따르면, 그들은 .NET3.5 SP1에 대한 병렬 확장 및 동시 데이터 구조의 묶음을 백 포트했습니다.

.NET 4 베타 2 용 샘플 세트가 있으며, 이는 평행 연장을 사용하는 방법에 대해 자세히 설명합니다.

방금 지난 주에 I/O를 수행하기 위해 32 개의 스레드를 사용하여 동시 정보를 테스트하는 데 보냈습니다. 광고 된대로 작동하는 것으로 보이며, 이는 엄청난 양의 테스트가 이에 입력했음을 나타냅니다.

편집하다: .NET 4 동시 소설 및 패턴.

Microsoft는 Paralell Programming 패턴이라는 PDF를 출시했습니다. .NET 4 동시 확장에 사용할 올바른 패턴과 피해야 할 방지 패턴에 사용할 수있는 올바른 패턴을 정말 멋진 세부 사항으로 설명했듯이 다운로드 할 가치가 있습니다. 여기있어.

기본적으로 당신은 새로운 동시 소설과 함께 가고 싶습니다. 상자 밖에서 스레드 안전 프로그램을 만들려면 적은 코드를 작성해야합니다.

캐시 된 컬렉션에 ConcurrentDictionary를 사용했습니다. 1 시간마다 다시 채워진 다음 여러 클라이언트 스레드가 비슷합니다. 솔루션에이 예제 스레드가 안전합니까? 의문.

우리는 그것을 바꾸는 것을 발견했습니다readonlydictionary전반적인 성능 향상.

ConcurrentDictionary 한 옵션 경우를 충족 모든 귀하의 스레드에 안전성 필요합니다.지 않는 경우,다른 말로 당신의 일을 아무것도 약간 복잡한 일반 Dictionary+lock 더 나은 옵션이 될 수 있습니다.예를 들어 말할 수 있습니다 추가 어떤 순서로 사전에 당신을 유지하려 업데이트된 총 금액의 주문입니다.할 수 있습 다음과 같이 코드를 작성합니다:

private ConcurrentDictionary<int, Order> _dict;
private Decimal _totalAmount = 0;

while (await enumerator.MoveNextAsync())
{
    Order order = enumerator.Current;
    _dict.TryAdd(order.Id, order);
    _totalAmount += order.Amount;
}

이 코드에 스레드에 안전합니다.여러 쓰레드를 업데이트 _totalAmount 수있는 필드에서 손상된 상태입니다.그래서 당신이 시도할 수 있습니로 보호 lock:

_dict.TryAdd(order.Id, order);
lock (_locker) _totalAmount += order.Amount;

이 코드는"안전한"하지만,여전히지 않는 스레드에 안전합니다.이 없다는 것을 보장 _totalAmount 과 일치하는 항목에서는 사전에 있습니다.다른 스레드를 읽으려고 이러한 값을 업데이트 UI 요소:

Decimal totalAmount;
lock (_locker) totalAmount = _totalAmount;
UpdateUI(_dict.Count, totalAmount);

totalAmount 에 해당하지 않을 수 있다수의 주문에 사전에 있습니다.표시된 통계를 잘못 될 수 있습니다.이 시점에서 깨달을 것을 확장해야 합 lock 을 포함하는 보호의 갱신이 사전

// Thread A
lock (_locker)
{
    _dict.TryAdd(order.Id, order);
    _totalAmount += order.Amount;
}

// Thread B
int ordersCount;
Decimal totalAmount;
lock (_locker)
{
    ordersCount = _dict.Count;
    totalAmount = _totalAmount;
}
UpdateUI(ordersCount, totalAmount);

이 코드가 완벽하게 안전하다,그러나 모든 사용의 이득 ConcurrentDictionary 은 사라졌습니다.

  1. 성능이 더 악화되보를 사용하여 일반 Dictionary, 기 때문에,내부 잠금부 ConcurrentDictionary 지금 낭비하고 중복되어 있습니다.
  2. 을 검토해야 하는 모든 코드에 대한 보호의 사용을 공유 변수입니다.
  3. 당신이 갇혀있으로 사용하여 어색한 API(TryAdd?, AddOrUpdate?).

그래서 내가 충고입니다:로 시작 Dictionary+lock, 고의 옵션을 유지 업그레이드 후 ConcurrentDictionary 으로 성능을 최적화하는 경우,이 옵션은 실제로 실행 가능합니다.많은 경우에 되지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top