升级锁中的饥饿问题

4
我正在尝试使用Boost的upgrade_lock(使用此示例),但我遇到了饥饿问题。
实际上,我正在使用此帖子中的代码,但我想要一个最新的讨论。在WorkerKiller之后,我运行了400个线程。我遇到了与提到的帖子的作者anoneironaut完全相同的问题。
我看到了来自Howard Hinnant的建议,但我不想包含更多外部代码(而且我现在无法编译他的代码),并且6个月后发布的评论指出“Boost现在使用公平的实现”(2012年12月3日)。 Boost 1.55文档指出:
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倍的提升实现的行为就像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无法重现这个问题。 - ComicSansMS
很有趣。我把我的代码复制到了这里。它在Debian(boost 1.55)和Ubuntu(boost 1.54)上进行了测试,使用clang++和g++编译。 - JonasVautherin
现在明白了,我没有意识到你正在使用upgrade_lock而不是普通的独占锁。 - ComicSansMS
1个回答

2
这是一个微妙的问题。区别涉及共享和可升级所有权的概念以及它们在Boost中的实现。
首先让我们搞清楚共享所有权和可升级所有权的概念。 对于SharedLockable,线程必须事先决定它是否想要更改对象(需要独占所有权)还是仅从中读取(共享所有权就足够了)。如果持有共享所有权的线程决定要更改对象,则首先必须释放其对对象的共享锁,然后构造一个新的独占锁。在这两个步骤之间,线程根本不持有对象的任何锁。试图从已经持有共享锁的线程构造独占锁将导致死锁,因为独占锁构造函数将阻塞,直到所有共享锁都被释放。 UpgradeLockable克服了这个限制,允许将共享锁升级为排他锁而不释放它。也就是说,线程始终保持对互斥量的活动锁定,防止其他线程在此期间获得排他锁。除此之外,UpgradeLockable仍然允许从SharedLockable进行所有操作,前者概念是后者的超集。你链接的问题只涉及SharedLockable概念。
根据Boost的规定,两个概念都不要求实现公平性。但是,作为SharedLockable的Boost最小实现shared_mutex确实提供了您问题中引用的公平性保证。请注意,这是概念实际要求的额外保证
不幸的是,可升级所有权的最小实现upgrade_mutex并未提供此额外的保证。它仍将共享所有权概念作为可升级所有权的一个要求,但由于符合规范的实现不需要公平性,因此它们不提供它。
正如评论中Howard指出的那样,Terekhov的算法可以轻松调整以适用于可升级锁,只是Boost实现目前不支持这一点。

好的,我明白了。但我不是很清楚为什么 UpgradeLockable 可以防止其他线程获得排他锁而不会阻止它们获得共享锁... 我可以使用独占互斥量来实现公平性,就像我在这个代码中所做的那样。无论如何,我以为它们都保证公平性,所以感谢您的开发;-)。 - JonasVautherin
@JonesV 思考一下:升级锁的行为类似于共享锁,唯一的例外是您同时只能有一个升级锁。因此,活动的升级锁确实可以防止其他排他锁,但不能阻止其他共享锁(就像普通的共享锁)。当涉及公平性时,问题在于即使您试图将升级锁转换为排他锁,这种行为也不会改变:其他共享锁仍然可以通过,即使您已经宣布需要排他锁。 - ComicSansMS
我理解这个。我的意思是,我不明白为什么它不能以公平的方式实现。我在之前的评论中链接的代码对我来说似乎比原始代码更公平,至少如此 =)。但也许由于额外的lock()/unlock()指令,它可能更昂贵... - JonasVautherin
1
Terekhov可以轻松地调整以适应可升级的互斥锁:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#upgrade_mutex_imp - Howard Hinnant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接