【问题标题】:Protecting against classic deadlock防止经典死锁
【发布时间】:2014-04-05 16:05:49
【问题描述】:

我已经为这个问题苦苦挣扎了大约 24 小时,但我似乎找不到解决办法。

假设我有一个 Foo 对象的集合:

public class Foo {
    public int SomeValue;

    public void Execute() {
        lock (this) {
            SomeValue++;
        }  
    }
}

// Instantiated elsewhere and filled with lots of Foos
private static ConcurrentCollection<Foo> fooCollection; 

现在我像这样为每个 Foo 调用 Execute(我有一台多核机器):

Parallel.ForEach(fooCollection, foo => foo.Execute());

现在假设我们添加第二种类型的Foo

public class Bar : Foo {
    private Foo myFriend;

    public new void Execute() {
        lock (this) {
            SomeValue++;
            lock (myFriend) {
                myFriend.SomeValue += SomeValue;
            }
        }  
    }
}

这个新的BarExecute 期间仍会自我锁定以保持一致性,并递增SomeValue。但它也会在其Foo 朋友上获得锁,因此myFriend 在更新myFriend.SomeValue 之前也以线程安全的方式更新。

我的问题是我可能有一个Bars 的循环引用链,例如

bar1.myFriend = bar2; bar2.myFriend = bar1;

在最坏的情况下:

  1. bar1.Execute() 锁定在 bar1
  2. bar2.Execute() 锁定在 bar2
  3. bar1.Execute() 等待锁定 bar2
  4. bar2.Execute() 等待锁定 bar1
  5. 死锁:(

所以,我的问题是:我该如何解决这个问题?

关于我的应用程序的一些实际指标:

  • 我真正的 'Execute()' 方法所做的事情比这个例子更有意义 - 请只是这个例子的表面价值:)
  • 我的收藏中有大约 50,000 个“Foos”。 Execute() 由 N 个线程在每个线程上并行调用,其中 N = 主机上的逻辑内核数。
  • 预期的争用率(例如,一个 Foo 正在等待另一个 Foo)很低,可能为 5%。如果没有循环引用的潜力,这个问题会很容易。速度并不是争用访问的真正问题,只是死锁情况。

一些注意事项:

  • 在本例中,我使用了lock (Monitor.Enter/Exit),但可以使用任何线程同步构造(例如状态变量和Interlocked 方法等)。
  • 应用程序对性能很敏感 - 所以对于 uncontended 情况,我需要尽可能快的速度。

我希望有一些我不知道的标准方法来解决这个问题。我尝试了各种体操,但每次我写出最坏的情况时,僵局仍然是潜在的。

提前致谢。 :)

【问题讨论】:

  • 是的,那会死锁。您必须使用 Monitor.TryEnter() 超时,以便您可以在失败时退避并让其他线程有机会完成。最坏情况下的性能不好。
  • 你不应该使用lock(this)。阅读Why it's bad...

标签: c# multithreading deadlock


【解决方案1】:

好吧,通常为了避免死锁,我们可以对资源进行优先级排序,因此请始终尝试首先锁定优先级较高的资源。

在您的情况下,它不像这里那么简单,对象是相同的或派生的类型。

所以解决这个问题的一个非常但扭曲的方法是为所有实例提供一个自动增量整数。任何具有更高值的实例将被视为具有更高的优先级

类似


foo p1 = this;
foo p2 = myfriend;
if(p1.priority < p2.priority)
{
    foo t = p2;
    p2 = p1;
    p1 = t;
}
lock(p1)
{
    lock(p2)
    {
        // here you can safely increment both p1 & p2 some value. 

    }
}

【讨论】:

  • 我自己也得出了类似的结论——如果这两个对象本质上是“相等的”,那么就没有办法在它们之间进行仲裁。所以是的,我认为你是对的 - 我必须为我的 Foos 添加一些人为的“价值”。有点不干净——但工作效率很高。
  • 是的,这里的优先级应该是基于实例而不是基于类来解决您的问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-27
  • 2011-08-11
  • 2013-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多