如何在 pthreads 的读写锁中防止写入器饥饿
Posted
技术标签:
【中文标题】如何在 pthreads 的读写锁中防止写入器饥饿【英文标题】:How to prevent writer starvation in a read write lock in pthreads 【发布时间】:2010-02-03 06:21:46 【问题描述】:我对 *nix 系统上的 POSIX Pthreads 中的读写锁有一些疑问,例如 Linux。
我想知道读写锁的默认偏差是什么,即它更喜欢读而不是写,反之亦然?它是否提供了一些 api 来更改此默认行为。
posix pthread 是否提供了一些 api 以便我们可以更改 pthread_rwlock_t 以防止写入器饥饿?根据我的阅读(如果我错了,请纠正我),默认实现偏向于读取器线程,因此写入器线程可能面临饥饿。
我已经阅读了 David Butenhof 的 Programming with Posix threads 一书中的 rw lock 示例实现。
我想知道 posix pthreads 如何处理编写器线程的饥饿?是否有一些 api 可以用来设置读写锁的属性,以防止写饥饿(我从未听说过)?还是用户必须处理这个问题?
如果您认为答案是由实现定义的,那么请举例说明它是如何在 Linux 中完成的,因为这就是我要寻找的。strong>
请注意,我只需要 *nix 系统的解决方案。不要认为我很粗鲁,但是发布一些特定于 windows 的代码对我来说毫无用处。
感谢大家的帮助和耐心:)
【问题讨论】:
使用互斥锁代替 rwlock 可以避免这个问题。如果争用较低,则在某些实现中也更快(例如从互斥锁和条件变量构造 rwlock 的实现)。 【参考方案1】:这确实取决于实现——所以既然您专门询问了 Linux,我的 cmets 指的是当前 glibc 中使用的 pthreads 的 NPTL 实现。
这里有两个相关但独立的问题。首先,有这种情况:
当前持有读锁,写入器正在等待。一个新线程尝试获取读锁。这里的默认操作是允许阅读器继续 - 有效地“跳过队列”超过编写器。但是,您可以覆盖它。如果您使用pthread_rwlockattr_setkind_np()
函数在您传递给pthread_rwlock_init()
的attr
上设置PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
标志,那么您的rwlock 将在上述情况下阻塞阅读器。
第二种情况是:
最后一个持有者释放锁,同时有读写器等待。在这种情况下,NPTL 总是会优先唤醒作者而不是读者。
综上所述,以上意味着如果你使用PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
标志,你的作者不应该被饿死(当然,现在连续不断的作者可以饿死读者。C'est la vie)。您可以通过检查pthread_rwlock_rdlock.c 和pthread_rwlock_unlock.c 中的来源(都非常易读1)来确认这一切。
请注意,还有一个PTHREAD_RWLOCK_PREFER_WRITER_NP
,但它似乎没有具有正确的效果 - 很可能是一个错误(也可能不是 - 请参阅comment by jilles below )。
1。 ...或者至少是,当我在 2010 年写这个答案时。最新版本的 NPTL 要复杂得多,我还没有重新进行分析。
【讨论】:
非常感谢。这就是我一直在寻找的。还有一件事,函数 pthread_rwlockattr_setkind_np() 是 POSIX api 还是只有 linux 特定的?我在我的 linux 系统上的 /usr/include/pthread.h 头文件中看不到它(可能是旧的)。 “np”代表什么?非常感谢:) 它在这里被列为 UNIX98 扩展:nptl.bullopensource.org/Tests/Optimization-level-in-nptl.html,但我认为这是一个错误,它实际上是一个 GNU 扩展(与以_np
结尾的所有其他函数一样)。当然在实践中它在其他地方是不可用的。我认为_np
是NPTL
的缩写,意思是“Native POSIX Threads Library”。我不确定到底是哪个版本的 glibc 引入了它,但我盒子上的 glibc 2.7 有它。也许可以在 glibc 邮件列表中询问?
嗯,我想我猜错了,_np
实际上可能只是意味着“不可移植”。
@R.. 我认为原因是PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
导致死锁,当线程锁定以进行递归读取并且编写器到达两个锁定操作之间时,这种死锁被认为比首选“更糟糕”读者多于作者,可能是因为 POSIX 需要递归读锁。如果死锁是固定的,那么不同类型的 rwlock 似乎没有什么理由。例如,如果有一个写入器在等待,FreeBSD 允许读取器进入,前提是线程已经拥有一个处于读取模式的 rwlock(它不会跟踪所有拥有的 rwlock,只跟踪有多少个)。
@R.. 以“一个线程可能持有多个并发读锁”开头的段落允许应用程序递归读锁。如果他们被允许,如果他们在相当正常的情况下陷入僵局,那就太奇怪了。另一方面,递归写锁可能会死锁或失败。以上是关于如何在 pthreads 的读写锁中防止写入器饥饿的主要内容,如果未能解决你的问题,请参考以下文章
Firestore限制每小时或每天读写一次,以防止用户在检查器中设置计时器