问题的ManualResetEvent不释放所有等待的线程一致线程、问题、ManualResetEvent

2023-09-07 02:58:49 作者:高傲的存在﹌

我想实现一个类,它使用用于保存从内部服务中检索数据的简单缓存。我使用的ManualResetEvent来阻止可能尝试刷新缓存的数据在同一时间与第一个线程成功信号他人进行,一旦数据被检索通过调用设置(),然后复位多线程()。当测试我注意到,有时所有的线程被释放,有时1个或多个不与留超时,仿佛我打电话复位前,所有的线程被释放。有人能解释什么,我做错了什么?

我已经包含以下的code进行砍伐的版本。

 私人布尔_u​​pdating;
    私人const int的WaitTimeout = 20000;
    私营的DateTime _lastRefresh;
    私有对象_cacheData;
    私人只读ManualResetEvent的_signaller =新的ManualResetEvent(假);

私人无效RefreshCachedData()
        {
            Console.WriteLine(ID为{0}:刷新缓存,Thread.CurrentThread.ManagedThreadId);
        如果(_updating)
        {
            Console.WriteLine(ID为{0}:高速缓存刷新正在进行中,等待信号,Thread.CurrentThread.ManagedThreadId);

            //另一个线程当前更新缓存所以等待一个信号以继续
            如果(!_signaller.WaitOne(WaitTimeout))
                Console.WriteLine(ID为{0}:线程超时({1} S)等待一个信号,即缓存已刷新,
                    Thread.CurrentThread.ManagedThreadId,WaitTimeout);

            Console.WriteLine(ID为{0}:信号:收到使用刷新缓存,Thread.CurrentThread.ManagedThreadId);
        }
        其他
        {
            尝试
            {
                _updating = TRUE;

                VAR的结果= _requestHandler.GetNewData();

                如果(!result.Success)
                {
                        Console.WriteLine(无法检索新的数据。);
                }
                其他
                {
                    //切换高速缓存的新数据
                    _cacheData = result.Data;

                    Console.WriteLine(
                        ID为{0}:缓存刷新的成功。
                        Thread.CurrentThread.ManagedThreadId);
                    Thread.sleep代码(8000);
                }
            }
            赶上(例外前)
            {
                Console.WriteLine(发生错误:{0},前);
            }
            最后
            {
                //设置刷新日期和时间,不管我们succeded与否
                _lastRefresh = DateTime.Now;
                _updating = FALSE;

                //信号的任何其他线程来进行,并使用刷新缓存
                Console.WriteLine(ID为{0}:信令其他线程刷新缓存,Thread.CurrentThread.ManagedThreadId);
                _signaller.Set();
                _signaller.Reset();
            }
        }
    }
 

解决方案

看起来你的线程没有得到来自ResetEvent释放复位前。

您可以通过创建活动开放和具有拳头线程输入方法重置解决问题。

另外,您可以通过做这样的事情避免的ManualResetEvent的行为反复无常:

 私有对象_latch =新的对象();
私人布尔_u​​pdating;

私人无效UpdateIfRequired()
{
    锁(_latch)
    {
        如果(_updating)
        {
            //在这里等着和短路了,当工作完成
            而(_updating)
                Monitor.Wait(_latch);

            返回;
        }

        _updating = TRUE;
    }

    //做很多昂贵的在这里工作

    锁(_latch)
    {
        _updating = FALSE;
        Monitor.PulseAll(_latch); //让其他线程去
    }
}
 
python 线程 为面试开辟VIP通道 测试 死锁 全局变量共享 守护主线程等 普通网友的博客 CSDN博客

查看此页面为一个伟大的解释,它是如何工作的 HTTP:// WWW .albahari.com /线程/ part4.aspx#_Signaling_with_Wait_and_Pulse

I'm trying to implement a class which uses a simple cache for holding data retrieved from an internal service. I'm using a ManualResetEvent to block multiple threads which may try to refresh the cached data at the same time with the first thread to succeed signalling the others to proceed once the data has been retrieved by calling Set() and then Reset(). When testing I've noticed that sometimes all of the threads are released and sometimes 1 or more are not and are left to time out, almost as if I am calling Reset before all of the threads were released. Can someone explain what I am doing wrong?

I've included a cut down version of the code below.

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

解决方案

Looks like your threads are not getting released from the ResetEvent before it is reset.

You could solve the problem by creating the event open and having the fist thread to enter your method reset it.

Alternatively you can avoid the vagaries of ManualResetEvent's behavior by doing something like this:

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
    }
}

Check out this page for a great explanation as to how this works http://www.albahari.com/threading/part4.aspx#_Signaling_with_Wait_and_Pulse