2012-01-26 2 views
0

가입 목록을 작성하려고합니다.스레드로부터 안전한 서브 스크립 션 목록을위한 최상의 데이터 구조가 필요합니까?

이 사전의 사용하는 것이 합리적이다> 가입자 -> 잡지 - 각 잡지의 목록을 가지고, 각 가입자의 목록을 가지고

출판사 발행인의

목록 : 이제 예제를 보자 C# 사전에서 사전 내에. 경합 조건없이 가입자를 추가/제거 할 때 전체 구조를 잠그지 않고이 작업을 수행 할 수 있습니까?

또한 코드가 C#에서 매우 빠르게 지저분 해져서 올바른 경로를 따라 가지 않을 것이라고 생각합니다. 이 작업을 수행하는 더 쉬운 방법이 있습니까? 다음은 생성자 있습니다 및 방법에 가입 :

참고 : 코드는

소스 ---> 유형 ---> 가입자

public class SubscriptionCollection<SourceT, TypeT, SubscriberT> 
{ 
// Race conditions here I'm sure! Not locking anything yet but should revisit at some point 

ConcurrentDictionary<SourceT, ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>> SourceTypeSubs; 

public SubscriptionCollection() 
{ 
    SourceTypeSubs = new ConcurrentDictionary<SourceT, ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>>(); 
} 

public void Subscribe(SourceT sourceT, TypeT typeT, SubscriberT subT) { 

    ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>> typesANDsubs; 
    if (SourceTypeSubs.TryGetValue(sourceT, out typesANDsubs)) 
    { 
     ConcurrentDictionary<SubscriberT, SubscriptionInfo> subs; 
     if (typesANDsubs.TryGetValue(typeT, out subs)) 
     { 

      SubscriptionInfo subInfo; 
      if (subs.TryGetValue(subT, out subInfo)) 
      { 
       // Subscription already exists - do nothing 

      } 
      else 
      { 
       subs.TryAdd(subT, new SubscriptionInfo()); 
      } 
     } 
     else 
     { 
      // This type does not exist - first add type, then subscription 
      var newType = new ConcurrentDictionary<SubscriberT, SubscriptionInfo>(); 
      newType.TryAdd(subT, new SubscriptionInfo()); 
      typesANDsubs.TryAdd(typeT, newType); 

     } 

    } 
    else 
    { 
     // this source does not exist - first add source, then type, then subscriptions 
     var newSource = new ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>(); 
     var newType = new ConcurrentDictionary<SubscriberT, SubscriptionInfo>(); 
     newType.TryAdd(subT, new SubscriptionInfo()); 
     newSource.TryAdd(typeT, newType); 
     SourceTypeSubs.TryAdd(sourceT, newSource); 
    }; 
} 
+0

이 질문 C#을 특이인가, 또는 당신은 어디서나 사용할 수있는 방법을 찾고있다? – svick

+0

아무 데나 정말로 .. 그럼 C# –

+0

에 적응할 수 있습니다. 질문이 C# 특정 적이기 때문에, 직접 사용할 수있는 .Net 프레임 워크에 클래스가 있기 때문에 묻습니다. – svick

답변

1

만약 위의 이름 대신 소스, 유형, 가입자를 사용 이미 사용하고있는 것처럼 ConcurrentDictionary을 사용하면 잠금이 필요하지 않습니다. 이미 잠금 처리가 필요합니다.

하지만 여전히 경쟁 조건과 어떻게 대처해야하는지 생각해야합니다. 다행히도 ConcurrentDictionary은 필요한 것을 정확하게 제공합니다. 예를 들어, 두 스레드가 동시에 존재하지 않는 소스에 등록하려고 시도하면 두 스레드 중 하나만 성공합니다. 하지만 그 이유는 TryAdd()이 추가가 성공했는지 여부를 반환하는 이유입니다. 반환 값을 무시할 수는 없습니다. false을 반환하면 다른 스레드가 해당 소스를 이미 추가 했으므로 이제 사전을 검색 할 수 있습니다.

또 다른 옵션은 the GetOrAdd() method입니다. 그것은 이미 존재하는 값을 검색하고 그것이 존재하지 않으면 생성합니다.

이 같은 코드를 재 작성 (그리고 그 길을 따라 훨씬 간단하게) 것 :

public void Subscribe(SourceT sourceT, TypeT typeT, SubscriberT subT) 
{ 
    var typesAndSubs = SourceTypeSubs.GetOrAdd(sourceT, 
     _ => new ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>()); 

    var subs = typesAndSubs.GetOrAdd(typeT, 
     _ => new ConcurrentDictionary<SubscriberT, SubscriptionInfo>()); 

    subs.GetOrAdd(subT, _ => new SubscriptionInfo()); 
} 
+0

GetOrAdd는 TryAdd의 반환 값을 확인하고 다시 시도 할 필요가 없으므로 코드를 훨씬 깔끔하게 만듭니다. (아마도 여러 번?) 각 스레드가 마침내 성공할 수 있도록합니다. 다른 사람이 다가오는 것이 아니라면 지금 upvoting하고 대답을 받아 들일 것입니다. 고마워요 !! –

+0

@HarryMexican, 당신은'TryAdd()'를 여러 번 할 필요가 없습니다. 다른 사람이 동일한 키가있는 항목을 이미 추가 한 경우에만 실패 할 수 있습니다. – svick

관련 문제