2010-06-18 5 views
1

나는이 코드 세그먼트를 가지고, 많은 것들을 간결하게 생략하지만 장면이 하나입니다이 코드를 안전하게하려면 어떤 옵션이 필요합니까?

public class Billing 
    { 
     private List<PrecalculateValue> Values = new List<PrecalculateValue>(); 

     public int GetValue(DateTime date) 
     { 
      var preCalculated = Values.SingleOrDefault(g => g.date == date).value; 
      //if exist in Values, return it 
      if(preCalculated != null) 
      { 
       return preCalculated; 
      } 

      // if it does not exist calculate it and store it in Values 
      int value = GetValueFor(date); 
      Values.Add(new PrecalculateValue{date = date, value = value}); 

      return value; 
     } 

     private object GetValueFor(DateTime date) 
     { 
      //some logic here 
     } 
    } 

나는 내가 이미 나중에 사용하기 위해 계산 된 모든 값을 저장 List<PrecalculateValue> Values을 가지고, 나는이 작업을 수행 주로 같은 클라이언트에 대해 두 번 다시 계산하기를 원하지 않기 때문에 각 계산에는 많은 작업이 필요하며 500-1000ms가 소요되며 구멍에 관련된 재귀 때문에 재사용 할 수있는 큰 기회가 있습니다 청구 클래스.

두 개의 서로 다른 클라이언트에 대해 두 개의 동시 계산을 수행하는 테스트를 수행 할 때까지이 모든 작업이 완벽하게 수행되며 컬렉션에 둘 이상의 결과가 발견되어 Values.Single(g => g.date == date).value 라인이 예외를 반환했습니다. 그래서 목록을 확인하고 동일한 목록에 두 클라이언트의 값을 저장합니다. 이 작은 문제를 피하려면 어떻게해야합니까? 우선

답변

5

음,이 라인 : 이후의 라인이 호출되지 않습니다 있도록

return Values.Single(g => g.date == date).value; 

그것을합니다. 여기에 코드를 약간 의역을 바꾼 것 같습니까? 당신이 당신의 Values 목록 쓰기를 동기화 할 경우

는 가장 간단한 방법은 목록 수정하고 코드 어디에서나 공통의 목적에 lock하는 것입니다 :

int value = GetValueFor(date); 

lock (dedicatedLockObject) { 
    Values.Add(new PrecalculateValue{date = date, value = value}); 
} 

return value; 

을하지만, 여기에 뭔가 다른 주목할 가치가 있습니다 :/DateTime 당 하나의 데이터 구조가 더 적합 할 것으로 보이므로 더 적절한 데이터 구조는 Dictionary<DateTime, PrecalculateValue> 일 것입니다. DateTime 키를 기반으로 번개가 빠른 O (1) 조회가 제공됩니다. List<PrecalculateValue> 당신이 찾고있는 것을 찾기 위해 반복해야합니다.

public class Billing 
{ 
    private Dictionary<DateTime, PrecalculateValue> Values = 
     new Dictionary<DateTime, PrecalculateValue>(); 

    private readonly commonLockObject = new object(); 

    public int GetValue(DateTime date) 
    { 
     PrecalculateValue cachedCalculation; 

     // Note: for true thread safety, you need to lock reads as well as 
     // writes, to ensure that a write happening concurrently with a read 
     // does not corrupt state. 
     lock (commonLockObject) { 
      if (Values.TryGetValue(date, out cachedCalculation)) 
       return cachedCalculation.value; 
     } 

     int value = GetValueFor(date); 

     // Here we need to check if the key exists again, just in case another 
     // thread added an item since we last checked. 
     // Also be sure to lock ANYWHERE ELSE you're manipulating 
     // or reading from the collection. 
     lock (commonLockObject) { 
      if (!Values.ContainsKey(date)) 
       Values[date] = new PrecalculateValue{date = date, value = value}; 
     } 

     return value; 
    } 

    private object GetValueFor(DateTime date) 
    { 
     //some logic here 
    } 
} 

그리고 조언의 마지막 조각 : 장소의 변화와

, 당신의 코드를 다음과 같이 보일 수 있습니다 그것은 특정 값의 더 이상 하나의 컬렉션에 존재하지 않는 것이 중요하지 않는 Single 메서드는 과도합니다. 오히려 첫 번째 값을 얻고 잠재적 인 중복을 무시하려는 경우 First은 더 안전하고 (예외 발생 가능성이 적음) 더 빠릅니다 (전체 컬렉션을 반복 할 필요가 없기 때문에).

+4

+1은 DateTime에 입력 된 사전을 사용합니다. 또한 .NET 4를 사용하는 경우 새 객체를 추가 할 때 잠금을 처리하는 새로운 ConcurrentDictionary가 있습니다. –

+0

hehe, 네, 그 논평 if's 실제로 논리의 일부입니다 =), 그것에 대해 부러워. 나중에 시도해 보도록하겠습니다. – JOBG

+0

ConcurrentDictionary, nice –

1

키 값 쌍의 목록은 비록 사전을 사용 권합니다이

public int GetValue(DateTime date) 
{ 

    var result = Values.Single(g => g.date == date) ?? GetValueFor(date); 

    lock (Values) 
    { 
     if (!Values.Contains(result)) Values.Add(result); 
    } 
    return result.value; 
} 

private PrecalculateValue GetValueFor(DateTime date) 
{ 
    //logic 
    return new PrecalculateValue() ; 
} 

같은 것을 사용할 수 있습니다.

관련 문제