C# ManualResetEvent未一致释放所有等待线程的问题

C# ManualResetEvent未一致释放所有等待线程的问题,c#,multithreading,.net-3.5,manualresetevent,C#,Multithreading,.net 3.5,Manualresetevent,我试图实现一个类,它使用一个简单的缓存来保存从内部服务检索到的数据。我正在使用ManualResetEvent来阻止多个线程,这些线程可能会尝试同时刷新缓存的数据,第一个线程成功地通过调用Set()和Reset()发出信号,指示其他线程在检索数据后继续。在测试时,我注意到有时所有线程都被释放,有时一个或多个线程没有被释放,并被超时,这几乎就像我在释放所有线程之前调用Reset一样。有人能解释一下我做错了什么吗 我在下面包含了代码的简化版本 private bool _updating;

我试图实现一个类,它使用一个简单的缓存来保存从内部服务检索到的数据。我正在使用ManualResetEvent来阻止多个线程,这些线程可能会尝试同时刷新缓存的数据,第一个线程成功地通过调用Set()和Reset()发出信号,指示其他线程在检索数据后继续。在测试时,我注意到有时所有线程都被释放,有时一个或多个线程没有被释放,并被超时,这几乎就像我在释放所有线程之前调用Reset一样。有人能解释一下我做错了什么吗

我在下面包含了代码的简化版本

    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();
            }
        }
    }

看起来在重置事件之前,您的线程没有从重置事件中释放

您可以通过创建打开的事件并让第一个线程进入您的方法重置它来解决问题

或者,您可以通过执行以下操作来避免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
    }
}

查看此页面,了解有关如何工作的详细说明

Thread.Sleep(8000)
WaitOne
听起来好像不能很好地发挥作用。线程睡眠只是为了测试,以确保所有其他线程进入等待状态,然后线程刷新缓存释放它们。这可能是一种误导,但如果我在设置和重置之间设置50毫秒的线程睡眠,在测试中,我还没有看到任何线程没有被释放。我可以在这里重现这种行为(我觉得很奇怪)。代码中的另一个问题可能是多个线程同时尝试读写更新的争用条件,这可能导致多个线程更新缓存。但这不是原因…这里有一个简短的复制: