【发布时间】:2012-02-12 00:04:17
【问题描述】:
我对多线程还很陌生,但已经被烧了好几次了。 现在我试图避免一些陷阱,但我很难做到以下几点:
考虑多个线程,线程A 生产者,因此writer,线程B,C,...消费者线程,因此reader。
所有共享一个公共缓冲区S
一些基础书籍建议为此场景引入所谓的ReadWriteLock。
多个并发读取是可能的,但显然只有一次写入。Boost 提供这样的锁,Qt 也是。
让我们假设两个函数 f1 和 f2 锁定 S 以供读取。
在线程 B 中调用 f1 并锁定读取,在 f1 内部调用 f2 并锁定再次读取,因此 嵌套锁定 是可能的。
现在考虑第一次执行 f1 并锁定读取的瞬间。如果线程 A 被调用并想写,他会被阻塞。
此外,如果在 f1 内部 f2 被调用并想要锁定它,则它不能锁定,因为阻塞的 writer 正在等待,在这种情况下,许多锁(例如 QReadWriteLock)也会进一步阻塞 readers。
因此,我们得到了一个非常讨厌的死锁,它是不可预测的,并且仅当writer 在 f1 的锁和 f2 的锁之间发生时才会发生。
我目前避免此类错误的方法是一个调试工具,它可以跟踪锁定并断言同一线程是否尝试锁定两次以进行读取,但这非常麻烦。 除此之外,我避免将大量代码放入锁中,这会有所帮助,但可能会被团队的其他成员忽略。
还有哪些其他功能可用于防止此类情况发生? 为什么 ReadWriteLocks 完全允许它们? 在设计阶段是否有一些通用的经验法则可以避免上述情况?
提前感谢您阅读长问题;-)
【问题讨论】:
-
多读单写锁不应该将单个递归锁算作“多个”,我认为...
-
好点,但讨厌的野兽 QReadWriteLock 确实做到了,这只花了我 3 个小时的调试时间 %-&
-
@Kerrek:我刚刚尝试了一个 Option QReadWriteLock::Recursive ,这样它似乎可以工作,但联机帮助页中的描述对我来说不是很清楚:“QReadWriteLock::Recursive 在这种模式下, 一个线程可以多次锁定同一个 QReadWriteLock 并且互斥锁不会被解锁,直到相应数量的 unlock() 调用被调用。QReadWriteLock::NonRecursive:在这种模式下,一个线程只能锁定一个 QReadWriteLock 一次。如果使用默认值:NonRecursive 并且即使这样,来自同一个线程的多个读取锁定也是可能的
-
好吧,对于任何类型的锁,你的有责任确保任何给定线程不会多次尝试获取锁,或以其他方式特别请求递归锁。我不知道 Qt,但你需要明确要求这个并不奇怪......
-
产品在生产者和消费者之间传递,复制成本高吗?
标签: c++ multithreading qt