2012-07-25 78 views
3

我试图实现一个类,它使用一个简单的缓存来保存从内部服务检索到的数据。我使用ManualResetEvent来阻塞多个线程,这些线程可能会尝试在第一个线程同时刷新缓存的数据,以通过调用Set(),然后调用Reset()来成功地通知其他人继续进行数据检索。在测试时我注意到有时候所有的线程都被释放了,有时候有一个或者更多的线程不会被超时,就好像我在所有的线程被释放之前调用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 2012-07-25 09:27:04

+0

线程睡眠只是为了测试的目的,以确保所有其他线程进入等待线程刷新缓存释放他们之前 – gouldos 2012-07-25 09:30:26

+1

这可能是一个红色的鲱鱼,但如果我把一个线程睡眠50ms之间的集并重置,我还没有看到在测试任何线程不会被释放。 – gouldos 2012-07-25 09:50:19

回答

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