死锁的常见原因是什么?

时间:2009-02-09 14:20:46

标签: deadlock

很难找到死锁,并且非常不舒服。

如何在代码中找到死锁的错误来源?有没有“死锁模式”?

在我的特殊情况下,它涉及数据库,但这个问题对每个死锁都是开放的。

12 个答案:

答案 0 :(得分:29)

更新:最近的MSDN文章Tools And Techniques to Identify Concurrency Issues也可能会引起关注


Stephen Toub在MSDN文章Deadlock monitor中指出了发生死锁所需的以下四个条件:

  • 有限数量的特定资源。对于C#中的监视器(当你使用lock关键字时使用的是什么),这个有限的数字是一个,因为监视器是一个互斥锁(意味着一次只有一个线程可以拥有一个监视器)。

  • 保留一个资源并请求另一个资源的能力。在C#中,这类似于在释放第一个锁之前锁定一个对象然后锁定另一个对象,例如:


lock(a)
{
...
    lock(b)
    {
            ...
    }
}
  • 没有抢占能力。在C#中,这意味着一个线程不能强制另一个线程释放锁。

  • 循环等待条件。这意味着存在一个循环的线程,每个线程都在等待下一个线程释放资源,然后才能继续。

他继续解释说避免死锁的方法是避免(或阻止)第四种情况。

  

Joe Duffy discusses several techniques   避免和检测死锁,   包括一个称为锁定练级。   在锁定调平中,分配锁定   数值和线程必须   只获得更高的锁   数字比他们已经锁定的数字   收购。这可以防止   循环的可能性。这也是   经常很难做好   今天典型的软件应用,   并且没有遵循锁定平衡   在每次锁定获取邀请   死锁。

答案 1 :(得分:14)

经典的死锁场景是A持有锁X并希望获得锁Y,而B持有锁Y并且想要获得锁X.既然两者都无法完成他们想要做的事情,两者都将永远等待(除非使用超时)。

在这种情况下,如果A和B以相同的顺序获取锁,则可以避免死锁。

答案 2 :(得分:8)

确保所有事务以相同的顺序影响表是避免最常见的死锁的关键。

例如:

交易A

UPDATE Table A SET Foo = 'Bar'
UPDATE Table B SET Bar = 'Foo'

交易B

UPDATE Table B SET Bar = 'Foo'
UPDATE Table A SET Foo = 'Bar'

这极有可能导致死锁,因为事务A 在表A上获得锁定,事务B 在表B上获得锁定,因此它们都没有得到锁定第二个命令直到另一个命令完成。

所有其他形式的死锁通常是在分配资源的情况下通过高强度使用和内部SQL Server死锁引起的。

答案 3 :(得分:8)

我的知识没有死锁模式(以及1​​2年编写大量多线程交易应用程序)。但是TimedLock类在查找代码中存在的死锁方面有很大的帮助而没有大量的返工。

http://www.randomtree.org/eric/techblog/archives/2004/10/multithreading_is_hard.html

基本上,(在dotnet / c#中)使用“使用TimedLock.Lock(xxx)”搜索/替换所有“lock(xxx)”语句

如果检测到死锁(在指定的超时内无法获取锁定,默认为10秒),则抛出异常。我的本地版本也会立即记录堆栈跟踪。走上堆栈跟踪(最好使用行号进行调试构建),你会立即看到在失败时保持了什么锁,以及它试图获得哪个锁。

在dotnet 1.1中,在所描述的死锁情况下,幸运的是所有锁定的线程都会同时抛出异常。因此,您将获得2+堆栈跟踪,以及解决问题所需的所有信息。 (2.0+可能已经在内部改变了线程模型而不是这么幸运,我不确定)

答案 4 :(得分:4)

是 - 当进程尝试以随机顺序获取资源时,会发生死锁。如果您的所有进程都尝试以相同的顺序获取相同的资源,那么即使没有消除死锁,也会大大减少死锁的可能性。

当然,这并不总是很容易安排......

答案 5 :(得分:2)

我建议您通过Herb Sutter阅读本文。它解释了死锁问题背后的原因,并在this article中提出了解决这一问题的框架。

答案 6 :(得分:1)

典型情况是更新计划不匹配(表格并不总是以相同的顺序更新)。但是,在高处理量下出现死锁并不罕见。

我倾向于接受死锁作为生活中的事实,它会在某一天发生,所以我让我的DAL准备好处理并重试死锁操作。

答案 7 :(得分:1)

最常见的(根据我不科学的观察)DB死锁场景非常简单:

  • 两个进程读取内容(例如DB记录),两者都获取关联资源(通常是数据库页面)的共享锁,
  • 两者都试图进行更新,尝试将锁升级为独家锁 - 瞧,死锁。

如果要在读取之后进行更新,则可以通过指定“FOR UPDATE”子句(或类似的,具体取决于您的特定RDBMS)来避免这种情况。这样,进程从一开始就获得了独占锁,使上述场景无法实现。

答案 8 :(得分:1)

当两个进程都在等待其他人完成之前发生的情况。结果是两个程序都挂起。 它最comonelly多任务和clint /服务器。

答案 9 :(得分:0)

当存在多个依赖锁时,会发生死锁。在一个线程和另一个线程尝试以相反的顺序锁定互斥锁。应该注意使用互斥锁来避免死锁。

确保在释放锁定后完成操作。如果您有多个锁,例如访问顺序是ABC,则释放顺序也应该是ABC。

答案 10 :(得分:0)

在我的上一个项目中,我遇到了SQL Server数据库中的死锁问题。找到原因的问题是,我的软件和第三方软件正在使用相同的数据库,并且正在使用相同的表。很难找到导致死锁的原因。我最后写了一个sql查询来找出哪些进程导致死锁的sql-Statements。您可以在此处找到该声明:Deadlocks on SQL-Server

答案 11 :(得分:0)

为避免死锁,有一种名为Banker's algorithm的算法。

此处还提供了有用的信息to avoid deadlock.