0

ManualResetEventhere を使用して、同時環境での内部アクションが同時に呼び出されないようにすることmyMethod()ができるどうかを判断しようとしています。

    static volatile bool _syncInitialized = false;  

    static ManualResetEvent _syncEvent = new ManualResetEvent(false);

    static object _syncLock = new object();

    void myMethod()
    {
        lock (_syncLock)       
        {
            if (!_syncInitialized)          // sync hasn't started, so 
            {
                _syncInitialized = true;    
                _syncEvent.Set();           // signal for the first time only 
            }
        }

        if (_syncEvent.WaitOne())           // wait for signal
        {
            _syncEvent.Close(); 
            _syncEvent = new ManualResetEvent(false); // reset to false 

            // actions that should be forced to run sequentially
        }
    }

編集- 潜在的にタイムアウトを追加できるようにしたいので、lock() の代わりに ManualResetEvent を使用していることに注意してください。

4

1 に答える 1

3

競合状態が発生する可能性が少なくとも 1 つあります。検討:

スレッド #1 は を実行し_syncEvent.WaitOne()て成功し、 を実行する前にスワップアウトされ_syncEvent.Close()ます。スレッド #2 が来て、 を実行しWaitOne()、これも成功します。

Close()別の問題は、明らかにリセットする方法として、新しいインスタンスを作成した後に呼び出していることです。次に、 を呼び出すClose()と、スレッドがスワップアウトされ、次のスレッドが来て を実行しようとしWaitOne()、オブジェクトが閉じられているために例外がスローされると想像してください。

イベントをリセットする場合は、 を呼び出しますReset()

おそらく、これを a で機能させることはできませんManualResetEvent。他の人が言ったように、ManualResetEvent相互排除ではなくシグナリングに使用されます。

将来的にタイムアウトを実装したいとおっしゃっています。スレッドがロックを一定期間待機し、ロックを取得できない場合に終了するだけの場合は、タイムアウト値を受け入れるMonitor.TryEnterオーバーロードのいずれかを使用します。例えば:

private object _syncObject = new Object();
void MyMethod()
{
    if (!Monitor.TryEnter(_syncObject, TimeSpan.FromSeconds(5)))
    {
        return; // couldn't get the lock
    }
    try
    {
        // got the lock. Do stuff here
    }
    finally
    {
        Monitor.Exit(); // release the lock
    }
}

のロックを本当に解放するかどうかについては、いくつかの議論がありますfinally。コードが例外をスローする場合、保護していたリソースが不完全または破損した状態になっている可能性があります (おそらく?)。その場合、他のスレッドがその上で動作することを望まないかもしれません。例外が発生した場合にロックを解放するかどうかは、アプリケーションの要件に合わせて行う必要がある設計上の決定です。

于 2012-12-29T20:58:33.370 に答える