防止代码死锁的锁定策略和技术
防止代码中死锁的常见解决方案是确保锁定顺序以通用方式发生,无论哪个线程正在访问资源。
例如,给定线程 T1 和 T2,其中 T1 访问资源 A,然后 B,T2 访问资源 B,然后访问 A。按所需顺序锁定资源会导致死锁。简单的解决方案是先锁定 A,然后再锁定 B,无论特定线程使用资源的顺序如何。
问题情况:
Thread1 Thread2
------- -------
Lock Resource A Lock Resource B
Do Resource A thing... Do Resource B thing...
Lock Resource B Lock Resource A
Do Resource B thing... Do Resource A thing...
可能的解决方案:
Thread1 Thread2
------- -------
Lock Resource A Lock Resource A
Lock Resource B Lock Resource B
Do Resource A thing... Do Resource B thing...
Do Resource B thing... Do Resource A thing...
我的问题是在编码中使用哪些其他技术、模式或常见做法来保证防止死锁?
The common solution to preventing deadlock in code is to make sure the sequence of locking occur in a common manner regardless of which thread is accessing the resources.
For example given threads T1 and T2, where T1 accesses resource A and then B and T2 accesses resource B and then A. Locking the resources in the order they are needed causes a dead-lock. The simple solution is to lock A and then lock B, regardless of the order specific thread will use the resources.
Problematic situation:
Thread1 Thread2
------- -------
Lock Resource A Lock Resource B
Do Resource A thing... Do Resource B thing...
Lock Resource B Lock Resource A
Do Resource B thing... Do Resource A thing...
Possible Solution:
Thread1 Thread2
------- -------
Lock Resource A Lock Resource A
Lock Resource B Lock Resource B
Do Resource A thing... Do Resource B thing...
Do Resource B thing... Do Resource A thing...
My question is what other techniques, patterns or common practices are used in coding to guarantee dead lock prevention?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您描述的技术不仅是常见的:它是一种已被证明始终有效的技术。不过,在用 C++ 编写线程代码时,您还应该遵循一些其他规则,其中最重要的可能是:
我可以继续说一段时间,但根据我的经验,使用线程的最简单方法是使用可能使用代码的每个人都熟知的模式,例如生产者/消费者模式:很容易解释,您只需要一个工具(队列)即可允许线程相互通信。毕竟,两个线程相互同步的唯一原因是允许它们进行通信。
更一般的建议:
下面的代码将会失败:
The technique you describe isn't just common: it's the one technique that has been proven to work all the time. There are a few other rules you should follow when coding threaded code in C++, though, among which the most important may be:
I could go on for a while, but in my experience, the easiest way to work with threads is using patterns that are well-known to everyone who might work with the code, such as the producer/consumer pattern: it's easy to explain and you only need one tool (a queue) to allow your threads to communicate with each other. After all, the only reason for two threads to be synchronized with each other, is to allow them to communicate.
More general advice:
The following code will fail:
在避免死锁方面,一致的锁定顺序几乎是首要和最后一个词。
有相关的技术,例如无锁编程(没有线程等待锁,因此不可能出现循环),但这实际上只是“避免不一致的锁定顺序”规则的一个特例 - 即它们通过避免所有锁定来避免不一致的锁定。不幸的是,无锁编程有其自身的问题,因此它也不是万能的。
如果您想稍微扩大范围,有一些方法可以在死锁发生时检测死锁(如果由于某种原因您无法设计程序来避免死锁),以及在死锁确实发生时打破死锁的方法(例如,始终使用使用超时锁定,或者强制死锁线程之一使其 Lock() 命令失败,甚至只是杀死死锁线程之一);但我认为它们都远不如简单地确保从一开始就不会发生僵局。
(顺便说一句,如果您想要一种自动方式来检查程序中是否存在潜在的死锁,请查看 valgrind 的 helgrind 工具。它将监视代码的锁定模式并通知您任何不一致的情况 - 非常有用)
Consistent ordering of locking is pretty much the first and last word when it comes to deadlock avoidance.
There are related techniques, such as lockless programming (where no thread ever waits on a lock, and thus there is no possibility of a cycle), but that's really just a special case of the "avoid inconsistent locking order" rule -- i.e. they avoid inconsistent locking by avoiding all locking. Unfortunately, lockless programming has its own issues, so it's not a panacea either.
If you want to broaden the scope a bit, there are methods for detecting deadlocks when they do occur (if for some reason you can't design your program to avoid them), and ways for breaking deadlocks when they do occur (e.g. by always locking with a timeout, or by forcing one of the deadlocked threads to have their Lock() command fail, or even just by killing one of the deadlocked threads); but I think they are all pretty inferior to simply making sure deadlocks cannot happen in the first place.
(btw if you want an automated way to check whether your program has potential deadlocks in it, check out valgrind's helgrind tool. It will monitor your code's locking patterns and notify you of any inconsistencies -- very useful)
另一种技术是事务性编程。但这并不常见,因为它通常涉及专用硬件(其中大多数目前仅在研究机构中)。
每个资源都会跟踪来自不同线程的修改。第一个提交对所有资源(正在使用的)更改的线程将赢得所有其他线程(使用这些资源)的回滚,以使用处于新提交状态的资源再次尝试。
阅读该主题的一个简单起点是事务内存。
Another technique is transactional programming. This though is not very common as it usually involves specialized hardware (most of it currently only in research institutions).
Each resource keeps track of modifications from different threads. The first thread to commit changes to all resources (it is using) wins all other thread (using those resources) get rolled back to try again with the resources in the new committed state.
A simplistic starting point for reading on the subject is transactional memory.
虽然不是您提到的已知序列解决方案的替代方案,Andrei Alexandrescu 写了一些用于编译时检查的技术,这些技术通过预期的机制完成了锁的获取。请参阅http://www.informit.com/articles/article.aspx?p= 25298
While not an alternative to the known-sequence solution you mention, Andrei Alexandrescu wrote about some techniques for compile time checks that acquisition of locks is done through the intended mechanisms. See http://www.informit.com/articles/article.aspx?p=25298
您问的是设计级别,但我会添加一些较低级别的编程实践。
You are asking about the design level, but I'll add some lower level, programming practices.