我用多种语言编程,但我不知道代码中存在死锁。
我认为这意味着它不会发生。
这是否经常发生(在编程中,数据库中的不),我应该关注它吗?
答案 0 :(得分:14)
如果两个条件成立,可能会出现死锁:你有多个theads,他们争夺多个资源。
你写多线程代码吗?您可以通过启动自己的线程来明确地执行此操作,或者您可以在创建线程的框架中工作,这样您就可以在多个线程中运行,而无需在代码中看到它。
示例:Java Servlet API。你编写了一个servlet或JSP。您部署到应用程序服务器。有几个用户访问了您的网站,因此也访问了您的servlet。服务器可能每个用户都有一个线程。
现在考虑如果在为请求提供服务时需要获取某些资源会发生什么:
if ( user Is Important ){
getResourceA();
}
getResourceB();
if (today is Thursday ) {
getResourceA();
}
// some more code
releaseResourceA();
releaseResoruceB();
在上面的人为举例中,考虑周四当重要用户的请求到来时会发生什么,并且或多或少地同时发生不重要用户的请求。
重要用户的线程获得Resoruce A并且想要B.不太重要的用户获得资源B并且想要A.也不会释放他们已经拥有的资源...死锁。
如果您正在编写明确使用同步的代码,这实际上很容易发生。最常见的是,我发现在使用数据库时会发生这种情况,幸运的是,数据库通常会出现死锁检测,因此我们可以找出错误。
防范僵局:
答案 1 :(得分:9)
很难想象它在现实中发生的频率(在生产代码中?在开发中?)并且这并不能真正了解多少代码易受攻击无论如何。 (通常情况下,僵局只会在非常特殊的情况下发生。)
我见过几次,虽然最近看到的是Oracle驱动程序(根本不在数据库中),因为终结器在另一个线程试图获取连接的同时运行。幸运的是,我发现了另一个错误,让我首先避免终结器运行...
基本上死锁几乎总是由于试图获得一个锁(B)同时持有另一个(A),而另一个线程反过来完全相同。如果一个线程正在等待B被释放,并且持有B的线程正在等待A被释放,则两个线程都不愿意让另一个继续。
确保您始终以相同的顺序获取锁(并以相反的顺序释放它们),并且在大多数情况下您应该能够避免死锁。
有些奇怪的情况你没有直接有两个锁,但它的基本原理是一样的。例如,在.NET中,您可以使用工作线程中的Control.Invoke
来更新UI线程上的UI。现在Invoke
等待更新处理后再继续。假设您的后台线程持有锁更新需要...再次,工作线程正在等待UI线程,但UI线程无法继续,因为工作线程持有锁。再次陷入僵局。
这是一种需要注意的模式。如果您确定只锁定了所需的位置,请锁定尽可能短的时间段,并记录所有代码的线程安全和锁定策略,应该能够避免死锁。但是,与所有线程主题一样,说起来容易做起来难。
答案 2 :(得分:4)
如果有机会,请查看Java Concurrency in Practice中的前几章。
死锁可能发生在任何并发编程情况中,因此它取决于您处理多少并发。并发编程的几个例子是:多进程,多线程和引入多线程的库。 UI框架,事件处理(例如计时器事件)可以作为线程实现。 Web框架可以生成线程以同时处理多个Web请求。使用多核CPU,您可能会比以前看到更多并发情况。
如果A正在等待B,并且B正在等待A,则循环等待会导致死锁。因此,它还取决于您编写的代码类型。如果使用分布式事务,则可以轻松导致该类型的方案。如果没有分布式交易,您就有可能冒银行账户偷钱。
答案 3 :(得分:3)
全部取决于您编码的内容。不使用锁定的传统单线程应用程序。不是真的。
具有多个锁的多线程代码会导致死锁。
我刚刚完成了重构代码,该代码使用了七个不同的锁而没有正确的异常处理。这有很多死锁问题。
答案 4 :(得分:3)
死锁的常见原因是当您有不同的线程(或进程)以不同的顺序获取一组资源时。
E.g。如果你有一些资源A和B,如果线程1获得A然后是B,并且线程2获得B然后获得A,则这是一个等待发生的死锁。
这个问题有一个简单的解决方案:让所有线程始终以相同的顺序获取资源。例如。如果你的所有线程按顺序获得A和B,你将避免死锁。
答案 5 :(得分:1)
死锁是指两个进程相互依赖的情况 - 一个无法在另一个进程之前完成。因此,如果您在任何时间运行多个代码流,则可能只会在代码中出现死锁。
开发多线程应用程序意味着您需要考虑死锁。单线程应用程序不太可能出现死锁 - 但并非不可能,显而易见的例子是您可能正在使用容易死锁的数据库。