【问题标题】:Why CAN this TryGetValue/TryUpdate on ConcurrentDictionary get deadlocked?为什么 ConcurrentDictionary 上的 TryGetValue/TryUpdate 会陷入僵局?
【发布时间】:2015-02-05 12:16:00
【问题描述】:

我在运行 IncrementModelClientReOrderCount 时遇到了死锁,但在我运行 IncrementModelClientReOrderCountLOCK 时问题就消失了。

不同之处在于 lock() 语句。

我假设使用 ConcurrentDictionary 已经减少了死锁的可能性。

我是否错误地使用了 ConcurrentDictionary:

public ConcurrentDictionary<Connection, ModelClient> ModelClients = new ConcurrentDictionary<Connection, ModelClient>();



    public bool IncrementModelClientReOrderCount(Connection mc)
    {
            ModelClient curValue;
            while (ModelClients.TryGetValue(mc, out curValue))
            {
                ModelClient curValue2 = curValue.Clone() as ModelClient; 
                curValue2.reOrderCount++;
                curValue2.DSP.seen = false;
                if (ModelClients.TryUpdate(mc, curValue2, curValue))
                    return true;

            }
            return false; 
    }


    public bool IncrementModelClientReOrderCountLOCK(Connection mc)
    {
        lock (ModelClients)
        {

            ModelClient curValue;
            while (ModelClients.TryGetValue(mc, out curValue))
            {
                ModelClient curValue2 = curValue.Clone() as ModelClient; 
                curValue2.reOrderCount++;
                curValue2.DSP.seen = false;
                if (ModelClients.TryUpdate(mc, curValue2, curValue))
                    return true;
            }
            return false; 
        }

    }


public class ModelClient : ICloneable
    {
        public string Symbol;
        public int Amount;
        public double Price;

        public ModelClient(string Symbol, int Amount, double Price)
        {
            this.Symbol = Symbol;
            this.Amount = Amount;
            this.Price = Price;
        }

        public object Clone() { return this.MemberwiseClone(); }
    }

【问题讨论】:

  • 为什么会认为死锁是由ConcurrentDictionary引起的?
  • @YuvalItzchakov 好的。我承认它可能是别的东西,但它确实发生在方法中。您是否看到其他可能是罪魁祸首的东西?
  • 这个问题显然非常需要死锁线程的堆栈跟踪。
  • 如果您所做的只是在其上调用方法,则 CD 永远不会死锁。那是因为 CD 永远不会在锁定状态下调用用户代码。还有其他事情在这里不可见。
  • @PeterRitchie CD 可以阻塞但不会死锁。

标签: c# concurrency deadlock concurrentdictionary


【解决方案1】:

“线程安全”集合本身是线程安全的,而不是编写集合时不存在的所有其他代码。 ConncurrentDictionary 也不是无锁的——这意味着它确实 锁定,因此有可能阻塞对其某些方法的调用。 (例如,两个相互依赖的线程向前推进,同时在ConcurrentDictionary 上调用阻塞方法)

锁的本质是防止两段代码同时执行并可能破坏状态——这意味着代码中几乎总是有不止一个地方使用lock,因此您可以编写代码重叠执行这两个块并导致死锁。

线程安全的集合仅意味着您可以在多个线程中使用它,并且它本身不会破坏自己的状态(您无权访问且无法保护自己的状态)。使用线程安全集合不会自动使您的所有代码都成为线程安全的,也不会让您不必了解潜在的死锁区域并使用您自己的线程安全原语进行补偿。

您没有为任何人提供足够的代码来准确说明您是如何陷入死锁的(或者它是否实际上是活锁)。但是,TryUpdate 可以阻塞并且如果另一个线程称为TryUpdate 具有相同的curValue,我希望TryUpdate 将返回 false 并且您的代码将再次尝试(即潜在的实时锁

在通过TryGetValue 获取值和使用TryUpdate 更新值之间似乎存在不变量。这个不变量是你的代码所独有的,你需要保护它。 lock 是一个好的开始;但您可能需要更好地理解它,然后才能接受 lock 是最佳解决方案。

【讨论】:

    猜你喜欢
    • 2020-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-14
    • 2017-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多