簡體   English   中英

如何防止以下C#代碼中的死鎖?

[英]How to prevent deadlocks in the following C# code?

以下C#類用於多線程環境。 我刪除了很多實際的代碼。 幾乎同時調用MethodA和MethodB時會出現問題。 IsDepleted屬性中的鎖定順序不能解決問題。 從IsDepleted屬性中刪除鎖(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
    }
}

獲取方法A中的Processing鎖定和方法B中的WaitingQueue鎖定(換句話說,使其看起來像第一個代碼塊)。 這樣,你總是以相同的順序拿鎖,你永遠不會死鎖。

這取決於您是否需要快速修復或嚴格修復。

快速修復只是在所有情況下使用一個鎖定對象。

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

簡化代碼並僅使用單個對象鎖定。 你也可以用以下方法替換你的鎖:

Monitor.TryEnter(處理中,1000)

這將給你1秒的超時。 基本上:

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

現在你不會停止死鎖,但你可以處理你沒有鎖定的情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM