2013-10-29 7 views
11

ReaderWriterLockSlim에 몇 가지 문제가 있습니다. 나는 그것이 어떻게 작동 하는지를 이해할 수 없다.ReaderWriterLockSlim 및 async await

내 코드 : 나는 < 1>에 쓰기 잠금을 입력하면

private async Task LoadIndex() 
    { 
     if (!File.Exists(FileName + ".index.txt")) 
     { 
      return; 
     } 
     _indexLock.EnterWriteLock();// <1> 
     _index.Clear(); 
     using (TextReader index = File.OpenText(FileName + ".index.txt")) 
     { 
      string s; 
      while (null != (s = await index.ReadLineAsync())) 
      { 
       var ss = s.Split(':'); 
       _index.Add(ss[0], Convert.ToInt64(ss[1])); 
      } 
     } 
     _indexLock.ExitWriteLock();<2> 
    } 

는, 디버거에서 나는 _indexLock.IsWriteLockHeldtrue 것을 볼 수 있지만, 실행 단계에 < 2> 내가 _indexLock.IsWriteLockHeldfalse_indexLock.ExitWriteLock입니다 참조 SynchronizationLockException에 "쓰기 잠금이 해제되지 않고 해제 중입니다."라는 메시지와 함께 예외가 발생합니다. 내가 뭘 잘못 했니?

+0

어떻게'_indexLock'가 초기화됩니까? 다른 스레드가'LoadIndex()'에 동시에 그것을 초기화 할 수 있습니까? –

+0

어쩌면 _indexLock에 대한 액세스 권한이있는 다른 스레드가 다시 초기화하는 중입니다 ... 다른 스레드에서 풀어 낼 수는 없지만 새로운 인스턴스에 모두 다시 초기화 될 수 있습니다 ... –

+0

스레드가 필요하지 않습니다. _indexLock을 덮어 씁니다. –

답변

24

ReaderWriterLockSlim은 thread-affine 잠금 유형이므로 일반적으로 asyncawait과 함께 사용할 수 없습니다.

당신은 WaitAsyncSemaphoreSlim를 사용하거나 (당신이 정말는 리더/라이터 잠금을 필요로하는 경우)를 사용한다 중 내 AsyncReaderWriterLock from AsyncEx 또는 Stephen Toub's AsyncReaderWriterLock.

+0

플랫폼에 따라 다르다고 말하는 것이 정확하지 않습니까? OP가 게시 한 코드에는 WPF 또는 콘솔 앱인지 언급되어 있지 않습니다. 콘솔 애플리케이션에서는 'SynchronizationContext'가 없으므로 작동하지 않습니다. 이 코드는 WPF 앱에서 잘 작동합니다. http://stackoverflow.com/questions/15882710/is-it-safe-to-use-readerwriterlockslim-in-an-async-method?noredirect=1&lq=1 –

+0

@DonBox : 아니요; 그 질문에 대한 나의 대답에서 언급했듯이, 비록 당신이 같은 스레드에서 재개하더라도, 당신은 그 잠금을 "고정"하고있는 동안 여전히 임의의 코드를 실행할 수있다. –

+0

오, 나는 그것을 올바르게 생각했습니다. "임의의 코드"가 의미하는 바를 설명 할 수 있습니까? WPF에서도 코드가 제대로 작동하지 않는다는 것을 의미합니까? 거기 자물쇠를 들고있어 뭐가 잘못 됐어? 대답에서 "보통" "보통 사용할 수없는"것은 무엇을 의미합니까? 감사! –

0

믿을 수 있고 가벼운 SemaphoreSlim을 사용하여 안전하게 리더/라이터 잠금 장치를 에뮬레이션하고 async/await의 이점을 유지할 수 있습니다. SemaphoreSlim을 작성하여 동시 읽기를 위해 자원을 잠그는 루틴 수와 동일한 사용 가능한 잠금 수를 제공하십시오. 각자는 평소와 같이 하나의 잠금을 요청할 것입니다. 귀하의 서적 루틴을 위해, 그 일을하기 전에 사용 가능한 모든 잠금을 요청했는지 확인하십시오.

그런 식으로, 독서 루틴이 그들 자신 사이에서만 자원을 공유하는 동안 당신의 작문 루틴은 항상 독자적으로 실행됩니다.

예를 들어 2 개의 읽기 루틴과 1 개의 쓰기 루틴이 있다고 가정합니다.

SemaphoreSlim semaphore = new SemaphoreSlim(2); 

async void Reader1() 
{ 
    await semaphore.WaitAsync(); 
    try 
    { 
     // ... reading stuff ... 
    } 
    finally 
    { 
     semaphore.Release(); 
    } 
} 

async void Reader2() 
{ 
    await semaphore.WaitAsync(); 
    try 
    { 
     // ... reading other stuff ... 
    } 
    finally 
    { 
     semaphore.Release(); 
    } 
} 

async void ExclusiveWriter() 
{ 
    // the exclusive writer must request all locks 
    // to make sure the readers don't have any of them 
    // (I wish we could specify the number of locks 
    // instead of spamming multiple calls!) 
    await semaphore.WaitAsync(); 
    await semaphore.WaitAsync(); 
    try 
    { 
     // ... writing stuff ... 
    } 
    finally 
    { 
     // release all locks here 
     semaphore.Release(2); 
     // (oh here we don't need multiple calls, how about that) 
    } 
} 

분명히이 방법은 동시에 실행할 수있는 읽기 루틴을 미리 알고있는 경우에만 작동합니다. 틀림없이이 코드를 너무 많이 작성하면 은 매우 ugly입니다.

+1

읽기/쓰기 의미론을 제공하지 않습니다. 판독기/기록기 잠금 장치의 개념은 원하는만큼 동시 판독기가 여러 개있을 수 있지만 쓰기 조작이 진행 중이면 판독기 또는 기타 작성자가 될 수 없다는 것입니다. 이 둘은 불필요하게 독자를 제한하며 한 번의 작업이 작성 될 때 독자 나 다른 작성자를 적절히 제한하지 않습니다. – Servy

+0

활성 작성자가있을 때 필자를 차단하고 활성 작성자가 없을 때 동시 판독자가 여전히 허용합니다. 독자가 얼마나 많은 독자를 가졌는지 알지 못하는 경우를 제외하고는 독자가 독자가해야 할 일입니다. ** ** 분명히 지적했듯이 (독자가 실제로 생각하면 주위를 둘러 쌀 수도 있지만 나는 당신을 놀라게하지 않을 것이다). 그것은 또한 당신의 표준을 위해 너무 우아하지 않을 수도 있지만, ** ** 유효한 해결책이고 나는 잘 테스트 된 잘 스트레스받는 높은 수요 서비스에서 그것을 사용했습니다. 나는 모두 단순해질 것이지만 각각은 그 자체로 ... – mycelo

+0

이것은 외부 의존성에 의지하지 않는 빠른 해결책에 나쁜 것은 아니다. 필요할 때 재사용을 위해 클래스에 랩핑해야한다. (모든 .waitAsync()를 여러 번 호출하고, 매번 사용했을 때 얼마나 많은 독자가 차지했는지 기억하고, 필요에 따라 사용할 때마다 변경한다.) 버그가 발생하기 쉬운). –