3

内部サービスから取得したデータを保持するために単純なキャッシュを使用するクラスを実装しようとしています。ManualResetEventを使用して、キャッシュされたデータを最初のスレッドと同時に更新しようとする複数のスレッドをブロックし、Set()を呼び出してからReset()を呼び出して、データが取得されたら他のスレッドに続行するように通知します。テストすると、すべてのスレッドが解放される場合と、1つ以上が解放されない場合があり、すべてのスレッドが解放される前にリセットを呼び出しているかのように、タイムアウトすることがあります。誰かが私が間違っていることを説明できますか?

以下のコードの縮小版を含めました。

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

1 に答える 1

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

于 2012-07-27T09:15:22.393 に答える