【问题标题】:Starvation with upgrade_lock饥饿与升级锁
【发布时间】:2014-03-13 11:24:20
【问题描述】:

我正在尝试使用 Boost 的 upgrade_lock(使用 this example,但我遇到了饥饿问题。

我实际上正在使用来自this post 的代码,但我想要一个最新的讨论。我在 WorkerKiller 之后运行了 400 个线程。我遇到了与上述帖子的作者 anoneironaut 完全相同的问题。

我已经看到了 Howard Hinnant 的提议,但我真的不想包含更多外部代码(而且我现在无法编译他的代码)和 6 个月后发表的评论声明“Boost 现在使用公平的实现”(2012 年 12 月 3 日)。

Boost 1.55 documentation 声明:

Note the the lack of reader-writer priority policies in shared_mutex. This is 
due to an algorithm credited to Alexander Terekhov which lets the OS decide 
which thread is the next to get the lock without caring whether a unique lock or 
shared lock is being sought. This results in a complete lack of reader or writer
starvation. It is simply fair.". 

并且归功于 Alexander Terekhov 的算法是 Howard Hinnant 所谈论的算法,所以我希望 1.55 boost 实现的行为类似于 Howard Hinnant 的回答,即并非如此。它的行为与问题中的完全一样。

为什么我的WorkerKiller会挨饿?

更新:在this code 上观察到:

  • Debian x64,Boost 1.55(Debian 版本和从源代码编译的版本),同时包含 clang++ 和 g++
  • Ubuntu x64,Boost 1.54,clang++ (3.4-1ubuntu1) 和 g++ (4.8.1-10ubuntu9)

【问题讨论】:

  • 您在什么编译器和平台上运行?我无法在 VC11 x64 构建上重现 Boost 1.55 的问题。
  • 这很有趣。我复制了我的代码here。它在 Debian (boost 1.55) 和 Ubuntu (boost 1.54) 上使用 clang++ 和 g++ 进行了测试。
  • 现在知道了,我没有意识到您使用的是upgrade_lock 而不是普通的独占锁。

标签: multithreading boost mutex


【解决方案1】:

这是一个微妙的。区别在于共享和可升级所有权的概念,以及它们在 Boost 中的实现。

让我们首先理清共享所有权和可升级所有权的概念。 对于SharedLockable,线程必须事先决定是要更改对象(需要独占所有权)还是只读取对象(共享所有权就足够了)。如果具有共享所有权的线程决定要更改对象,它首先必须释放其在对象上的共享锁,然后构造一个新的排他锁。在这两个步骤之间,线程在对象上完全没有锁。尝试从已经持有共享锁的线程构造排他锁会死锁,因为排他锁构造函数将阻塞,直到所有共享锁都被释放。

UpgradeLockable 克服了这个限制,允许将共享锁升级为独占锁而不释放它。即线程始终保持对互斥锁的活动锁,同时禁止其他线程获得排他锁。除此之外,UpgradeLockable 仍然允许来自 SharedLockable 的所有操作,前者概念是后者的超集。 question you linked to 只关注 SharedLockable 概念。

Boost 指定的这两个概念都不要求实现是公平的。但是,shared_mutex 是 Boost 对 SharedLockable 的最小实现,它确实提供了您问题中引用的公平保证。请注意,这是对概念实际要求的附加保证

不幸的是,可升级所有权的最小实现,upgrade_mutex,没有提供这个额外的保证。它仍然将共享所有权概念作为可升级所有权的要求来实现,但由于符合标准的实现不需要公平,因此它们不提供它。

作为pointed out by Howard in the comments,Terekhov 的算法也可以进行微调以使用可升级锁,只是 Boost 实现目前不支持这一点。

【讨论】:

  • 好的,我明白了。但是我真的不明白为什么 UpgradeLockable 可以阻止其他线程获得独占锁而不是阻止它们获得共享锁......我可以使用独占互斥体来实现公平,like I did in this code。无论如何,我认为他们都保证了公平性,所以感谢您的发展;-)。
  • @JonesV 可以这样想:升级锁的行为类似于共享锁,只是一次只能拥有一个升级锁。因此,主动升级锁确实阻止了其他独占锁,但不会阻止其他共享锁(就像普通的共享锁一样)。公平性的问题是,当您尝试将升级锁转换为排他锁时,这种行为不会改变:即使您已经宣布需要排他锁,其他共享锁仍然会通过。
  • 我明白这一点。我的意思是我不明白为什么它不能以公平的方式实施。我在之前的评论中链接的代码对我来说似乎比原来的更公平,至少 =)。但也许它更贵,因为额外的lock()/unlock() 指令......
  • Terekhov 可以进行微调以使用可升级的互斥锁:open-std.org/jtc1/sc22/wg21/docs/papers/2007/…
猜你喜欢
  • 1970-01-01
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
  • 2020-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多