我正在寻找一个好的 C++ 读/写锁。我们有一个不常写者和许多常读者的用例,并希望对此进行优化。最好我想要一个跨平台的解决方案,但是只有一个 Windows 是可以接受的。
12 回答
从 C++ 17 (VS2015) 开始,您可以使用标准:
#include <shared_mutex>
typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock > WriteLock;
typedef std::shared_lock< Lock > ReadLock;
Lock myLock;
void ReadFunction()
{
ReadLock r_lock(myLock);
//Do reader stuff
}
void WriteFunction()
{
WriteLock w_lock(myLock);
//Do writer stuff
}
对于较旧的编译器版本和标准,您可以使用boost创建读写锁:
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
typedef boost::shared_lock< Lock > ReadLock;
较新版本的boost::thread具有读/写锁(1.35.0 及更高版本,显然以前的版本无法正常工作)。
它们具有名称shared_lock
,unique_lock
和upgrade_lock
, 对 a 进行操作shared_mutex
。
使用标准的预测试、预构建的东西总是好的(例如,另一个答案建议的 Boost),但这并不是很难自己构建的东西。这是从我的一个项目中提取的一个愚蠢的小实现:
#include <pthread.h>
struct rwlock {
pthread_mutex_t lock;
pthread_cond_t read, write;
unsigned readers, writers, read_waiters, write_waiters;
};
void reader_lock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
if (self->writers || self->write_waiters) {
self->read_waiters++;
do pthread_cond_wait(&self->read, &self->lock);
while (self->writers || self->write_waiters);
self->read_waiters--;
}
self->readers++;
pthread_mutex_unlock(&self->lock);
}
void reader_unlock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
self->readers--;
if (self->write_waiters)
pthread_cond_signal(&self->write);
pthread_mutex_unlock(&self->lock);
}
void writer_lock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
if (self->readers || self->writers) {
self->write_waiters++;
do pthread_cond_wait(&self->write, &self->lock);
while (self->readers || self->writers);
self->write_waiters--;
}
self->writers = 1;
pthread_mutex_unlock(&self->lock);
}
void writer_unlock(struct rwlock *self) {
pthread_mutex_lock(&self->lock);
self->writers = 0;
if (self->write_waiters)
pthread_cond_signal(&self->write);
else if (self->read_waiters)
pthread_cond_broadcast(&self->read);
pthread_mutex_unlock(&self->lock);
}
void rwlock_init(struct rwlock *self) {
self->readers = self->writers = self->read_waiters = self->write_waiters = 0;
pthread_mutex_init(&self->lock, NULL);
pthread_cond_init(&self->read, NULL);
pthread_cond_init(&self->write, NULL);
}
pthreads
不是真正的 Windows 原生,但总体思路就在这里。这种实现稍微偏向于作家(一大群作家可以无限期地饿死读者);writer_unlock
如果您希望平衡相反,只需修改即可。
是的,这是 C 而不是 C++。翻译是留给读者的练习。
编辑
Greg Rogers 指出 POSIX 标准确实指定了pthread_rwlock_*
. 如果您没有,这将无济于事pthreads
,但它让我想起了:Pthreads-w32应该可以工作!无需将此代码移植到非pthreads
自己使用,只需在 Windows 上使用 Pthreads-w32,并pthreads
在其他任何地方使用原生代码。
无论您决定使用什么,都将您的工作负载与简单锁进行基准测试,因为当没有争用时,读/写锁往往比简单互斥锁慢 3-40 倍。
这里有一些参考
编辑:MSDN 杂志链接不再可用。CodeProject 文章现在可以在https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks上找到,并且总结得很好。我还发现了一个关于Compound Synchronization Objects的新 MSDN 链接。
MSDN 上有一篇关于读写锁的文章,介绍了它们的一些实现。它还引入了 Slim 读取器/写入器锁,这是一种随 Vista 引入的内核同步原语。还有一篇关于比较不同实现的CodeProject 文章(包括 MSDN 文章的那些)。
C++17 支持std::shared_mutex
. 它在MSVC++ 2015和 2017 中受支持。
英特尔线程构建模块还提供了几个 rw_lock 变体:
http://www.threadingbuildingblocks.org/
他们有一个 spin_rw_mutex 用于非常短的争用时间和一个 queueing_rw_mutex 用于较长时间的争用。前者可用于对性能特别敏感的代码。后者在性能上与 Boost.Thread 或直接使用 pthreads 提供的性能更具可比性。但是配置文件以确保哪个是您的访问模式的胜利。
我可以推荐ACE 库,它提供了多种锁定机制并被移植到各种平台。
根据问题的边界条件,您可能会发现以下类很有用:
ACE_RW_Process_Mutex
ACE_Write_Guard
和ACE_Read_Guard
ACE_Condition
Boost.Thread自 1.35.0 版以来已经支持读写锁。这样做的好处是该实现非常跨平台,经过同行评审,实际上是即将到来的 C++0x 标准的参考实现。
http://www.codeproject.com/KB/threads/ReaderWriterLock.aspx
这是一个适用于大多数任务的良好且轻量级的实现。
用于 Win32 的多读取器、单写入器同步锁类,作者 Glenn Slayde
您可以复制 Sun 出色的ReentrantReadWriteLock。它包括可选的公平性、锁定降级,当然还有可重入性等功能。
是的,它是用 Java 编写的,但即使您不了解任何 Java,您也可以轻松阅读并将其转换为 C++。我链接到的文档包含此实现的所有行为属性,因此您可以确保它符合您的要求。
如果没有别的,它是一个指南。