2014-06-08 1 views
1

나는 현재 스레드로부터 안전하지 않습니다 코드가의 이야기 :보장 스레드 안전 - 두 가지 전략

public byte[] GetImageByteArray(string filepath, string contentType, RImgOptions options) 
{    
    //Our unique cache keys will be composed of both the image's filepath and the requested width 
    var cacheKey = filepath + options.Width.ToString(); 
    var image = HttpContext.Current.Cache[cacheKey]; 

    //If there is nothing in the cache, we need to generate the image, insert it into the cache, and return it 
    if (image == null) 
    { 
     RImgGenerator generator = new RImgGenerator(); 
     byte[] bytes = generator.GenerateImage(filepath, contentType, options); 
     CacheItem(cacheKey, bytes); 
     return bytes; 
    } 
    //Image already exists in cache, serve it up! 
    else 
    { 
     return (byte[])image; 
    } 
} 

CacheItem() 방법 검사의 최대 캐시 크기에 도달되었는지 여부를 확인하기 위해, 그리고 이 경우, 그것은 캐시 항목을 제거하기 시작합니다 : 다른 스레드가 그것을 참조하는 것처럼 하나 개의 스레드가 캐시에서 항목을 제거 할 수

//If the cache exceeds its max allotment, we will remove items until it falls below the max 
while ((int)cache[CACHE_SIZE] > RImgConfig.Settings.Profile.CacheSize * 1000 * 1000) 
{ 
    var entries = (Dictionary<string, DateTime>)cache[CACHE_ENTRIES]; 
    var earliestCacheItem = entries.SingleOrDefault(kvp => kvp.Value == entries.Min(d => d.Value)); 
    int length = ((byte[])cache[earliestCacheItem.Key]).Length; 
    cache.Remove(earliestCacheItem.Key); 
    cache[CACHE_SIZE] = (int)cache[CACHE_SIZE] - length; 
} 

때문에, 나는 두 가지 옵션을 생각할 수 있습니다 :

옵션 1 : 잠금

lock (myLockObject) 
{ 
    if(image == null){ **SNIP** } 
} 

옵션 2 : 지역 변수이 옵션은 모두 오버 헤드가

var image = HttpContext.Current.Cache[cacheKey] != null ? HttpContext.Current.Cache[cacheKey].MemberwiseClone() : null; 

에 얕은 사본을 할당합니다. 첫 번째 코드는 한 번에 하나씩 코드 블록에 입력되도록 스레드를 강제 실행합니다. 두 번째는 사소한 크기 일 수있는 새로운 객체를 메모리에 생성해야합니다.

여기에 사용할 수있는 다른 전략이 있습니까?

+0

이 코드에는 문제가 없습니다. 캐시에서 항목을 제거해도 내가 처리 할 수있는 한 처리하지 못합니다. 따라서 캐시에서 제거하면 참조가 제거됩니다. 명시 적으로 처리하지 않는 한 참조하는 객체는 영향을받지 않아야합니다.Cache 자체는 스레드로부터 안전하기 때문에 사용자가 찾고있는 동안 제거되는 객체에 대해 걱정할 필요가 없습니다. –

+0

web.config에서 캐시의 최대 크기를 설정할 수 있다는 것도 알고 있습니까? 그리고이 크기에 도달하면 asp.net 자체가 캐시를 잘라 내기 시작합니다? 나는 왜 너 자신이 이것을 할 필요성을 느끼는지 왜 확신하지 못한다 ... –

+0

@ErikFunkenbusch 나는 네가 말하는 것에 진리가 있다고 믿는다. 로컬의 image 변수는 캐시가 가리키는 객체를 참조로 유지할 것이다. 쓰레기 수거를 위해. 로컬 변수가 사용되지 않은 (그러나 아마도 있어야하는) 예제를 살펴 보았고 대신 두 개의 별도 작업에서 캐시를 다시 쿼리하므로 잠금이 사용되었습니다. 어쨌든 두 가지 식욕을 돋우는 옵션이 필요하지 않다는 점을 분명히 말하여 주셔서 감사합니다. –

답변

0

캐시 솔루션의 순수한 일관성을 제공하려면 응용 프로그램 속도를 낮추면서 리소스를 잠 가야합니다.

일반적으로 응용 프로그램 논리에 따라 캐싱 전략을 제공해야합니다.

  • 확인 슬라이딩 윈도우 캐싱 : 일부 시간 범위 때 오래된 항목 동안 - 다른 스레드의 잠금을 줄일 수는 - 좋은 당신 확실히 다시 사용됩니다하지 다른 캐시 항목의 대규모 확산이있을 때.
  • 은 적어도 자주 사용하는 전략을 사용하는 것을 고려 : 도달 최대 캐시 크기는 동안 적어도 을 사용하는 항목을 제거해야 - 당신은 캐시 내용의 자주 동일한 부품을 치는 여러 클라이언트가 동안 가장 역할을한다.

어떤 유형이 더 나은 BL 유형인지 확인하고 사용하십시오. 잠금 문제는 전혀 제거되지 않지만 올바른 선택은 경주 조건을 상당히 제거합니다.

다른 스레드 간의 공유 리소스를 줄이려면 전체 컬렉션이 아닌 각 항목에 읽기 및 쓰기 잠금을 사용하십시오. 이렇게하면 실적이 향상됩니다.

염두에 두어야 할 또 다른 고려 사항 - 이미지가 이미 캐시 된 상태에서 동일한 경로의 이미지 내용이 디스크에서 물리적으로 변경된 경우 (다른 이미지가 동일한 이름으로 저장 됨) 실수로 관련 데이터를 제공하지 않습니다.

희망이있었습니다.