5

我已经用多种语言进行了编程,但我不知道我的代码中存在死锁。

我认为这意味着它不会发生。

这是否经常发生(在编程中,而不是在数据库中)以至于我应该关注它?

4

6 回答 6

14

如果两个条件为真,就会出现死锁:您有多个头,并且它们争夺多个资源。

你写多线程代码吗?您可以通过启动自己的线程来明确地执行此操作,或者您可能在一个框架中工作,其中线程是在您的视线之外创建的,因此您在多个线程中运行而您在代码中却没有看到它。

一个例子:Java Servlet API。您编写一个 servlet 或 JSP。您部署到应用服务器。几个用户访问了您的网站,因此访问了您的 servlet。服务器可能每个用户都有一个线程。

现在考虑如果在为您想要获取一些资源的请求提供服务时会发生什么:

if ( user Is Important ){
     getResourceA();
}

getResourceB();

if (today is Thursday ) {
    getResourceA();
} 


// some more code

releaseResourceA();
releaseResoruceB();

在上面的人为示例中,考虑在星期四当重要用户的请求到达时可能会发生什么,并且或多或少同时到达一个不重要的用户的请求。

重要用户的线程获得资源 A 并想要 B。不太重要的用户获得资源 B 并想要 A。两者都不会放弃他们已经拥有的资源……死锁。

如果您正在编写显式使用同步的代码,这实际上很容易发生。最常见的是我在使用数据库时看到它发生,幸运的是数据库通常有死锁检测,所以我们可以找出我们犯了什么错误。

死锁防御:

  1. 以明确的顺序获取资源。在上面的示例中,如果总是在资源 B 之前获得资源 A,则不会发生死锁。
  2. 如果可能,请使用超时,这样您就不会无限期地等待资源。这将允许您检测争用并应用防御 1。
于 2009-09-01T06:27:55.033 回答
9

很难知道它在现实中发生的频率(在生产代码中?在开发中?)而且这并不能很好地说明有多少代码容易受到它的影响。(通常情况下,死锁只会在非常特定的情况下发生。)

我见过一些情况,尽管我看到的最近一次是在 Oracle 驱动程序中(根本不在数据库中),因为终结器与另一个试图获取连接的线程同时运行。幸运的是,我发现了另一个错误,它让我避免了终结器首先运行......

基本上死锁几乎总是由于试图获取一个锁(B)同时持有另一个锁(A)而另一个线程反过来做完全相同的事情。如果一个线程正在等待 B 被释放,而持有 B 的线程正在等待 A 被释放,则两者都不愿意让另一个继续。

确保您始终以相同的顺序获取锁(并以相反的顺序释放它们),并且在大多数情况下您应该能够避免死锁。

有一些奇怪的情况,你没有直接拥有两个锁,但这是相同的基本原理。例如,在 .NET 中,您可以使用Control.Invoke工作线程来更新 UI 线程上的 UI。现在Invoke等到更新处理完毕后再继续。假设您的后台线程持有更新需要的锁...再次,工作线程正在等待 UI 线程,但 UI 线程无法继续,因为工作线程持有锁。再次陷入僵局。

这是一种需要注意的模式。如果您确保只锁定您需要的地方,锁定尽可能短的时间,并记录所有代码的线程安全和锁定策略,您应该能够避免死锁。然而,像所有线程主题一样,说起来容易做起来难。

于 2009-09-01T06:25:43.103 回答
4

如果您有机会,请查看Java Concurrency in Practice的前几章。

在任何并发编程情况下都可能发生死锁,因此这取决于您处理的并发量。并发编程的几个例子是:多进程、多线程和引入多线程的库。UI 框架,事件处理(如定时器事件)可以实现为线程。Web 框架可以产生线程来同时处理多个 Web 请求。使用多核 CPU,您可能会看到比以前更多的并发情况。

如果A在等待B,B在等待A,循环等待导致死锁。因此,它还取决于您编写的代码类型。如果您使用分布式事务,则很容易导致这种情况。如果没有分布式交易,您就有银行账户被盗钱的风险。

于 2009-09-01T06:40:53.603 回答
3

一切都取决于你在编码什么。不使用锁定的传统单线程应用程序。并不真地。

具有多个锁的多线程代码会导致死锁。

我刚刚完成了使用七个不同锁而没有适当异常处理的代码的重构。这有许多僵局问题。

于 2009-09-01T06:29:09.510 回答
3

死锁的一个常见原因是当您有不同的线程(或进程)以不同的顺序获取一组资源时。

例如,如果你有一些资源 A 和 B,如果线程 1 获得 A 然后 B,线程 2 获得 B 然后 A,那么这是一个等待发生的死锁。

这个问题有一个简单的解决方案:让所有线程始终以相同的顺序获取资源。例如,如果您的所有线程都按该顺序获取 A 和 B,您将避免死锁。

于 2009-09-01T06:43:16.250 回答
1

死锁是两个进程相互依赖的情况——一个进程不能在另一个进程之前完成。因此,如果您在任何时候运行多个代码流,您的代码可能只会出现死锁。

开发多线程应用程序意味着您需要考虑死锁。单线程应用程序不太可能出现死锁 - 但并非不可能,明显的例子是您可能正在使用可能会出现死锁的数据库。

于 2009-09-01T06:29:13.847 回答