【问题标题】:Understanding and avoiding the dangers of nested locks in multithreaded applications了解和避免多线程应用程序中嵌套锁的危险
【发布时间】:2012-02-12 00:04:17
【问题描述】:

我对多线程还很陌生,但已经被烧了好几次了。 现在我试图避免一些陷阱,但我很难做到以下几点:

考虑多个线程,线程A 生产者,因此writer,线程BC,...消费者线程,因此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


【解决方案1】:

也许您可以使用 QReadWriteLock 中的 tryReadLock 函数。防止死锁。

如果它未能锁定读取.. 则解锁读取互斥锁。

在某些情况下,它可能会丢失要读取的数据,如果您不想丢失任何要读取的数据,可以通过放置标志或 ID 号或其他东西来识别您失败的读取作业并将其放入堆栈,所以在写线程完成他的工作后,您将能够从失败的内容开始恢复您的阅读内容。它会最大限度地减少您丢失数据的情况..

我希望它会有所帮助。

对不起我的英语不好。

【讨论】:

    猜你喜欢
    • 2021-08-23
    • 1970-01-01
    • 1970-01-01
    • 2014-06-29
    • 2023-03-08
    • 2012-11-24
    • 2021-11-02
    • 1970-01-01
    • 2011-04-09
    相关资源
    最近更新 更多