2017-12-08 12 views
0

나는 약 AsyncLock library에 대해 읽고 있는데, 몇 가지 질문이 있습니다. 링크에서캐시 키당 asp.net 적절한 비동기 잠금

예제 코드의 다음 줄이 있습니다

private readonly AsyncLock _mutex = new AsyncLock(); 
public async Task DoStuffAsync() 
{ 
    using (await _mutex.LockAsync()) 
    { 
    await Task.Delay(TimeSpan.FromSeconds(1)); 
    } 
} 

내가 내 ASP.NET 컨트롤러에서이 코드를 재사용하려고 노력을하지만, 나는 몇 가지 의문이있다.

위에서 언급 한 시나리오는 캐시 키가 항상 같은 바 식 잠금을 다루지 만 아래 예처럼 동적 캐시가있는 경우에는 어떻습니까? ArticleId1은 사용자로드를 잠급니다. ArticleId2. 각 캐시 키는 자신의 AsyncLock _mutex이어야합니까?

또한 다중 사용자가 잠금을 공유한다는 사실 때문에 mutex를 정적으로 변환하는 작업을 올바르게 수행 했습니까?

private static readonly AsyncLock _mutexIndex = new AsyncLock(); 
public async Task<IActionResult> Index(int articleId) 
{ 

    var key = CacheKeysFor.Article.ById(articleId); 
    ArticleModel cacheEntry; 
    cacheEntry = _cache.Get<ArticleModel>(key); 

    if (cacheEntry == null) 
    { 
     using (await _mutexIndex.LockAsync()) 
     { 
      if (cacheEntry == null) 
      { 
       cacheEntry = SomeDatabaseCall 
       _cache.Set(key, cacheEntry, TimeSpan.FromSeconds(60)); 
      } 

     } 
    } 

    return View(cacheEntry); 
} 
+0

그리고 '_cache'는 무엇입니까? 그게 MemoryCache인가요? – Evk

+0

@Evk는 MemoryCache이지만, 의존성 주입을 통해 전환하면 쉽게 Redis가 될 수 있습니다. – Robert

답변

2

이 경로를 사용하려는 경우 각 키에 대해 별도의 잠금이 필요합니다. 당신은 ConcurrentDictionary으로, 예를 들어 그것을 달성 할 수있다 : 키 잠금이 시간이 지남에 축적됩니다

static ConcurrentDictionary<string, Lazy<AsyncLock>> _keyLocks = new ConcurrentDictionary<string, Lazy<AsyncLock>>(); 

public async Task<IActionResult> Index(int articleId) 
{  
    var key = CacheKeysFor.Article.ById(articleId); 
    ArticleModel cacheEntry; 
    cacheEntry = _cache.Get<ArticleModel>(key); 

    if (cacheEntry == null) 
    { 
     var keyLock = _keyLocks.GetOrAdd(key, _ => new Lazy<AsyncLock>(() => new AsyncLock())).Value; 
     using (await keyLock.LockAsync()) 
     { 
      if (cacheEntry == null) 
      { 
       cacheEntry = SomeDatabaseCall 
       _cache.Set(key, cacheEntry, TimeSpan.FromSeconds(60)); 
      } 

     } 
    } 

    return View(cacheEntry); 
} 

참고. 많은 사람들이 없으면 큰 문제는 아닙니다. 수백만 개가있는 경우 수시로 자물쇠 수집을 지울 수 있습니다. 그렇게하는 것은 안전하지 않으며 여러 스레드가 보호 된 블록을 입력하도록 허용 할 수 있습니다. 그러나이 구체적인 경우에는 (값 비싼 데이터베이스 호출을 피하기 위해 잠금을 사용하기 때문에) 문제가되지 않는 것으로 보입니다.

+0

이 적당합니다. 앱 풀을 하루에 한 번 이상 다시 시작하기 때문에 누적은 문제가되지 않습니다. 감사합니다 – Robert

+0

알겠습니다. 이것은 동일한 키에 대해 여러 개의 잠금을 만들거나 작성하지 못하도록하는 방법입니다. – Robert

+1

@ 로버트 더 이상의 생각을 한 후에 위의 내 코멘트가 잘못되었습니다. 게으름이 없다고해도 안전 할 것입니다. 게으르지 않은'GetOrAdd'의 문제점은 당신이 여러 번 통과 한 델리게이트를 실행할 수 있다는 것입니다. 따라서이 경우 동일한 키에 액세스하는 동안 여러 개의 AsyncLock을 만들 수 있습니다. 그러나 두 AsyncLock 중 하나만 GetOrAdd 호출에서 반환되고 다른 하나는 무시됩니다. AsyncLock은 일회용이 아니기 때문에, 일부는 생성 및 폐기 될 수있는 문제가별로 없습니다. – Evk