6

同じスレッドがロックを解放しようとしているかどうかを検出することは可能ですか? コードには次のような場所がたくさんあります。

try
{
    try
    {
       if(!Monitor.TryEnter(obj, 2000)) 
       { 
            throw new Exception("can not lock"); 
       }
    }
    finally
    {
       Monitor.Exit(obj);
    }
}
catch
{
    //Log
}

上記のコードは非常に単純化されており、実際にはカスタム オブジェクト (ロック マネージャー) にある Enter ステートメントと Exit ステートメントです。

問題は、その構造では、SynchronizationLockException「終了」しようとすると、ロックに成功しないスレッドのように見えるため、最終的に解放しようとすることです。

問題は、Monitor.Exit を作成したスレッドが Monitor.Enter を作成したスレッドと同じかどうかをどのように知ることができるかということです。
CurrentThread.Id を使用して入力と終了を同期できると思いましたが、十分に「安全」かどうかはわかりません。

4

3 に答える 3

19

問題は、Monitor.Exit を作成したスレッドが Monitor.Enter を作成したスレッドと同じかどうかをどのように知ることができるかということです。

私の知る限り、簡単にはできません。どのスレッドがモニターを所有しているかはわかりません。

ただし、これは単なるコーディング上の問題です。モニターを解放してはならないときにも解放を試みないように、コードを変更する必要があります。したがって、上記のコードは次のように書き直すことができます。

if (!Monitor.TryEnter(obj, 2000))
{
    throw new Exception(...);
}
try
{
    // Presumably other code
}
finally
{
     Monitor.Exit(obj);
}

または、さらに良いことに、.NET 4 を使用している場合は、パラメーターを受け入れるオーバーロードを使用します。TryEnterret

bool gotMonitor = false;
try
{
    Monitor.TryEnter(obj, ref gotMonitor);
    if (!gotMonitor)
    {
        throw new Exception(...);
    }
    // Presumably other code
}
finally
{
    if (gotMonitor)
    {
        Monitor.Exit(obj);
    }
}
于 2012-12-30T09:39:43.783 に答える
2

Monitor.Exitの呼び出しをtry-catchに入れるのは「ダーティ」(ダーティ?)だったと思うので、「ダーティを取り除く」ことを試みる非常に簡単なアイデアがあります。ロックは同じスレッドに対して再入可能であり、1つのスレッドが正常に取得された場合、解放される前に、別のスレッドからの試行は失敗します。あなたが次のようなことを考えることができるように:

public void Exit(object key) {
    if(!IsActive) {
        return;
    }

    if(LockDictionary.ContainsKey(key)) {
        var syncObject=LockDictionary[key];

        if(Monitor.TryEnter(syncObject.SyncObject, 0)) {
            SetLockExit(syncObject);
            Monitor.Exit(syncObject.SyncObject);
            Monitor.Exit(syncObject.SyncObject);
        }
    }
}

Monitor.Exitを2回呼び出します。これは、2回ロックするためです。1つはコードの外側にあり、もう1つはここにあります。

于 2012-12-31T10:11:35.717 に答える
1

これは古い質問であることは知っていますが、とにかくこれが私の答えです。私なら、try-finally コンストラクトを if 内に移動します。

try
{
    if(Monitor.TryEnter(obj, 2000))
    {
        try
        {
            // code here
        }
        finally
        {
            Monitor.Exit(obj);
        }
    }
    else
    {
        throw new Exception("Can't acquire lock");
    }
}
catch
{
    // log
}
于 2018-01-19T11:44:59.213 に答える