2

次の C# クラスは、マルチスレッド環境で使用されます。実際のコードの大部分を削除しました。この問題は、MethodA と MethodB をほぼ同時に呼び出すと発生します。IsDepleted プロパティのロックの順序は問題を解決しません。IsDepleted プロパティから lock(WaitingQueue) を削除するとデッドロックが解決されますが、この解決策では、別のスレッドが WaitingQueue.Count == 0 ステートメントと Processing.Count == 0 ステートメントの間で WaitingQueue に項目を追加/削除するときに問題が発生します。

using System.Collections.Generic;

class Example
{
    bool IsDepleted
    {
        get
        {
            lock (Processing)
            {
                lock (WaitingQueue)
        {
                    return WaitingQueue.Count == 0
             && Processing.Count == 0;
        }
            }
        }
    }

    private readonly List<object> Processing = new List<object>();
    private readonly Queue<object> WaitingQueue = new Queue<object>();

    public void MethodA(object item)
    {
        lock (WaitingQueue)
        {
            if (WaitingQueue.Count > 0)
            {
                if (StartItem(WaitingQueue.Peek()))
                {
                    WaitingQueue.Dequeue();
                }
            }
        }
    }

    public void MethodB(object identifier)
    {
        lock (Processing)
        {
            Processing.Remove(identifier);
            if (!IsDepleted)
            {
                return;
            }
        }
    //Do something...
    }

    bool StartItem(object item)
    {
        //Do something and return a value
    }
}
4

3 に答える 3

4

迅速な修正が必要か、厳密な修正が必要かによって異なります。

簡単な修正は、すべての場合に 1 つのロック オブジェクトを使用することです。

例えばprivate readonly object _lock = new object();

そして、それをロックするだけです。ただし、状況によっては、許容できる以上にパフォーマンスに影響を与える可能性があります。

つまり、コードは次のようになります。

using System.Collections.Generic;

class Example
{
    private readonly object _lock = new object();

    bool IsDepleted
    {
        get
        {
            lock (_lock)
            {
                return WaitingQueue.Count == 0
                 && Processing.Count == 0;
            }
        }
    }

    private readonly List<object> Processing = new List<object>();
    private readonly Queue<object> WaitingQueue = new Queue<object>();

    public void MethodA(object item)
    {
        lock (_lock)
        {
            if (WaitingQueue.Count > 0)
            {
                if (StartItem(WaitingQueue.Peek()))
                {
                    WaitingQueue.Dequeue();
                }
            }
        }
    }

    public void MethodB(object identifier)
    {
        lock (_lock)
        {
            Processing.Remove(identifier);
            if (!IsDepleted)
            {
                return;
            }
        }
        //Do something...
    }

    bool StartItem(object item)
    {
        //Do something and return a value
    }
}
于 2009-01-14T18:52:28.957 に答える
3

メソッド A の Processing ロックとメソッド B の WaitingQueue ロックを取得します (つまり、コードの最初のブロックのように見せます)。そうすれば、常に同じ順序でロックを取得でき、デッドロックが発生することはありません。

于 2009-01-14T18:50:19.820 に答える
2

コードを簡素化し、単一のオブジェクトのみを使用してロックします。ロックを次のように置き換えることもできます。

Monitor.TryEnter(処理中,1000)

これにより、1 秒のタイムアウトが発生します。したがって、本質的に:

        if (Monitor.TryEnter(Processing, 1000))
        {
            try
            {
                //do x
            }
            finally
            {
                Monitor.Exit(Processing);
            }
        }

これで、デッドロックは止まりませんが、ロックを取得できない場合は処理できます。

于 2009-01-14T19:00:03.903 に答える