如何防止以下C#代码中的死锁?

时间:2009-01-14 18:46:56

标签: c# .net multithreading deadlock

以下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
    }
}

3 个答案:

答案 0 :(得分:3)

获取方法A中的Processing锁定和方法B中的WaitingQueue锁定(换句话说,使其看起来像第一个代码块)。这样,你总是以同样的顺序拿锁,你永远不会死锁。

答案 1 :(得分:3)

这取决于您是否需要快速修复或严格修复。

快速修复只是在所有情况下使用一个锁定对象。

e.g。 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
    }
}

答案 2 :(得分:2)

简化代码并仅使用单个对象锁定。您也可以用以下代码替换锁:

Monitor.TryEnter(处理1000)

这会给你1秒的超时。基本上是这样的:

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

现在你不会停止死锁,但你可以处理你没有锁定的情况。