2012-04-23 5 views
1

업데이트/읽기를 위해 여러 백그라운드 스레드가 액세스하는 목록이 있습니다. 업데이트 작업에는 삽입과 삭제가 모두 포함됩니다.동시 사전 액세스의 효율성 향상 C#

동기화 문제없이이 작업을 동시에 수행하려면 클래스의 개인 읽기 전용 개체에 대한 잠금을 사용하고 있습니다.

데이터를 읽을 때 목록을 잠글 시간을 최소화하기 위해 데이터를 복제하고 깊은 복제본을 반환하고 업데이트 삽입/삭제를 위해 사전 잠금을 해제합니다.

이 때문에 목록을 읽을 때마다 내 서비스의 메모리 사용량이 증가합니다.

주목할 점은 삽입/삭제가 목록을 포함하는 클래스 내부에 있다는 점입니다. 그러나 읽는 것은 대중의 소비를위한 것입니다.

내 질문은 :

는 어떤 방식으로, 나는 목록을 복제 방지하고 여전히/쓰기 읽기 잠금을 사용하여 읽기 위해 동시에 사용할 수 있습니까?

public class ServiceCache 
    { 
     private static List<Users> activeUsers; 
     private static readonly object lockObject = new object(); 
     private static ServiceCache instance = new ServiceCache(); 

     public static ServiceCache Instance 
     { 
      get 
      { 
       return instance; 
      } 
     } 

     private void AddUser(User newUser) 
     { 
      lock (lockObject) 
      { 
       //... add user logic 
      } 
     } 

     private void RemoveUser(User currentUser) 
     { 
      lock (lockObject) 
      { 
       //... remove user logic 
      } 
     } 

     public List<Users> ActiveUsers 
     { 
      get 
      { 
       lock (lockObject) 
       { 
        //The cache returns deep copies of the users it holds, not links to the actual data. 
        return activeUsers.Select(au => au.DeepCopy()).ToList(); 
       } 
      } 
     } 
    } 
+0

작업을 하나의 잠금을 사용 불변이어야한다 목록, 당신의 작업이 의미있게 동기화되도록하는 방법은 무엇입니까? –

+0

Eric, 딥 복사본은 캐시의 스냅 샷으로 만 사용하도록되어 있으며 변경하면 실제 캐시에 반영되어서는 안됩니다. – EndlessSpace

+2

사이드 노트 : "시간을 최소화하기 위해 ... 나는 그것의 진품을 만듭니다": 이것은 "모든 악의 조기 최적화 루트"라는 문구가 존재하는 정확한 이유입니다. 중요하지 않은 개체의 딥 클론은 빠른 작업이 아닐 것입니다. 당신은 항상 당신의 "최적화"가 실제로 무엇인지 이해해야합니다. –

답변

6

ConcurrentDictionary 클래스를 사용해야하고 저장할 객체 인 Users 각각에 대한 키를 만드는 것처럼 들립니다. 그런 다음 사용자 추가/갱신이처럼 간단하게 :

_dictionary.AddOrUpdate("key", (k, v) => 
    { 
     return newUser; 
    }, (k, v) => 
    { 
     return newUser; 
    }); 

및 다음 제거, 당신이 할 것 : 사람들의 목록뿐만 아니라 매우 쉬울 것이다 얻기

Users value = null; 
_dictionary.TryRemove("key", out value); 

을, 당신이해야 할 일이 있기 때문에 :

return _dictionary.Values.Select(x => x.Value).ToList(); 

바로 그 순간에 사전 내용의 사본을 돌려 주어야합니다.

그리고 .NET 런타임에서 스레딩을 처리하도록합니다.

+1

사실, 단지'.Values'를 반환 할 수 있습니다. 이것은 copy-on-read입니다. – SLaks

+0

흥미 롭다, 나는 그것을 몰랐다! 내가 그것을 읽었을 때 그것은 전체 감각을 만든다. = D – Tejs

5

동시 읽기를 허용하려면 리더기 잠금 장치를 사용할 수 있습니다.

그러나 ConcurrentDictionary과 thread-safe 불변 값을 사용하는 것이 훨씬 빠르며 모든 동기화를 제거하십시오.

+0

자물쇠가이 클래스에 국한되어 있어도 문제가되지 않지만 읽기 위해 잠그는 개체에 액세스하고 있습니까? – EndlessSpace

+0

무슨 뜻인지 잘 모르겠습니다. – SLaks

1

이로 인해 목록을 읽을 때마다 내 서비스의 메모리 사용량이 증가합니다.

왜? 발신자가 참조를 해제하지 않습니까? 사전의 내용이 바뀔 수 있으므로 필요합니다.

당신이 복사로 수행하는 작업은 동시 데이터 구조 (예 : 호출자가 참조를 보유 할 수 없다는 점을 제외하고는 copy-on-write 콜렉션이 작동합니다.

다른 방법의 몇 :

  • 반환 컬렉션까지 모든 발신자에게 동일한 복사본이 수정됩니다.독립적 인 스레드가 자신의 깊은 사본에서 삭제하는 경우 반환되는 컬렉션

  • 은 호출자가 복사에서 원하는 수있는 모든 기능을 노출하고 원래 목록

관련 문제