2012-07-25 2 views
3

내부 서비스에서 검색 한 데이터를 보유하기 위해 간단한 캐시를 사용하는 클래스를 구현하려고합니다. 첫 번째 스레드가 Set() 및 Reset() 호출하여 데이터를 검색 한 다른 진행 신호를 성공하려면 동시에 캐시 된 데이터를 새로 고치려면 시도 할 수있는 여러 스레드를 차단하려면 ManualResetEvent 사용하고 있습니다. 테스트 할 때 때로는 모든 스레드가 해제되고 때로는 1 개 이상이 해제되고 모든 스레드가 해제되기 전에 Reset을 호출하는 것처럼 시간 초과가 남아 있음을 알았습니다. 누군가 내가 잘못하고있는 것을 설명 할 수 있습니까?ManualResetEvent가 대기중인 모든 스레드를 일관되게 해제하지 않음

나는 아래 코드의 축소판을 포함 시켰습니다.

private bool _updating; 
    private const int WaitTimeout = 20000; 
    private DateTime _lastRefresh; 
    private object _cacheData; 
    private readonly ManualResetEvent _signaller = new ManualResetEvent(false); 

private void RefreshCachedData() 
     { 
      Console.WriteLine("ThreadId {0}: Refreshing Cache", Thread.CurrentThread.ManagedThreadId); 
     if (_updating) 
     { 
      Console.WriteLine("ThreadId {0}: Cache Refresh in progress, waiting on signal.", Thread.CurrentThread.ManagedThreadId); 

      // another thread is currently updating the cache so wait for a signal to continue 
      if (!_signaller.WaitOne(WaitTimeout)) 
       Console.WriteLine("ThreadId {0}: Thread timed out ({1}s) waiting for a signal that the cache had been refreshed", 
        Thread.CurrentThread.ManagedThreadId,WaitTimeout); 

      Console.WriteLine("ThreadId {0}: Signal recieved to use refreshed cache.", Thread.CurrentThread.ManagedThreadId); 
     } 
     else 
     { 
      try 
      { 
       _updating = true; 

       var result = _requestHandler.GetNewData(); 

       if (!result.Success) 
       { 
         Console.WriteLine("Failed to retrieve new data."); 
       } 
       else 
       { 
        // switch the cache with the new data 
        _cacheData = result.Data; 

        Console.WriteLine(
         "ThreadId {0}: Cache refresh success.", 
         Thread.CurrentThread.ManagedThreadId); 
        Thread.Sleep(8000); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine("Error occured: {0}", ex); 
      } 
      finally 
      { 
       // Set the refresh date time regardless of whether we succeded or not 
       _lastRefresh = DateTime.Now; 
       _updating = false; 

       // signal any other threads to to carry on and use the refreshed cache 
       Console.WriteLine("ThreadId {0}: Signalling other threads that cache is refreshed.", Thread.CurrentThread.ManagedThreadId); 
       _signaller.Set(); 
       _signaller.Reset(); 
      } 
     } 
    } 
+0

'에 Thread.sleep (8000) '와'WaitOne'은 멋지게 잘 어울리지 않는 것처럼 들립니다. – leppie

+0

스레드 수면은 스레드가 캐시를 새로 고치기 전에 다른 모든 스레드가 대기 상태가되었는지 확인하기 위해 테스트를 목적으로 만있었습니다. – gouldos

+1

이것은 빨간색 청어가 될 수 있지만 세트 사이에 50ms의 스레드 수면을 넣으면 재설정하고 나면 아직 릴리스되지 않은 모든 스레드를 테스트 해보아야합니다. – gouldos

답변

2

초기화되기 전에 스레드가 ResetEvent에서 해제되지 않는 것처럼 보입니다.

이벤트를 연 상태로 만들고 주먹 스레드로 메소드를 시작하면 문제를 해결할 수 있습니다.

은 다른 방법은 다음과 같이 수행하여으로 ManualResetEvent의 행동의 변덕을 방지 할 수 있습니다로이 어떻게 작동하는지에 좋은 설명이 페이지

private object _latch = new object(); 
private bool _updating; 

private void UpdateIfRequired() 
{ 
    lock (_latch) 
    { 
     if (_updating) 
     { 
      //wait here and short circuit out when the work is done 
      while (_updating) 
       Monitor.Wait(_latch); 

      return; 
     } 

     _updating = true; 
    } 

    //do lots of expensive work here 

    lock (_latch) 
    { 
     _updating = false; 
     Monitor.PulseAll(_latch); //let the other threads go 
    } 
} 

체크 아웃 http://www.albahari.com/threading/part4.aspx#_Signaling_with_Wait_and_Pulse

관련 문제