Reader/Writer Locks in C++

I'm looking for a good reader/writer lock in C++. We have a use case of a single infrequent writer and many frequent readers and would like to optimize for this. Preferable I would like a cross-platform solution, however a Windows only one would be acceptable.

asked Oct 28, 2008 at 18:26 Matt Price Matt Price 45.1k 9 9 gold badges 39 39 silver badges 44 44 bronze badges

13 Answers 13

Since C++ 14 (VS2015) you can use the standard:

#include typedef std::shared_mutex Lock; typedef std::unique_lock < Lock >WriteLock; // C++ 11 typedef std::shared_lock < Lock >ReadLock; // C++ 14 Lock myLock; void ReadFunction() < ReadLock r_lock(myLock); //Do reader stuff >void WriteFunction() < WriteLock w_lock(myLock); //Do writer stuff >

For older compiler versions and standards you can use boost to create a read-write lock:

#include #include typedef boost::shared_mutex Lock; typedef boost::unique_lock < Lock >WriteLock; typedef boost::shared_lock < Lock >ReadLock; 
107 9 9 bronze badges answered Jun 23, 2011 at 7:15 Yochai Timmer Yochai Timmer 48.8k 24 24 gold badges 153 153 silver badges 187 187 bronze badges

This looks like this will still starve the write thread given tons of read threads and one write thread.

Commented Nov 16, 2020 at 18:03

You don't need 3 separate locks -- std::shared_mutex already supports ReadWriteLock functionality by itself. To acquire an exclusive (writer) lock, use ".lock()", to acquire a shared (reader) lock use ".lock_shared()"

Commented Sep 11, 2022 at 16:05

@GavinRay Yes, but with using those structs, you get wrapping RAII objects that will ensure proper resource management and ensure that the locks are actually released in the end of the scope.

Commented Sep 11, 2022 at 16:40 Oh I see, so they're alternatives to using std::lock_guard in each function where you acquire locks? Commented Sep 11, 2022 at 16:42

Newer versions of boost::thread have read/write locks (1.35.0 and later, apparently the previous versions did not work correctly).

They have the names shared_lock , unique_lock , and upgrade_lock and operate on a shared_mutex .

5,685 7 7 gold badges 45 45 silver badges 56 56 bronze badges answered Oct 28, 2008 at 18:36 Greg Rogers Greg Rogers 36.3k 17 17 gold badges 68 68 silver badges 94 94 bronze badges We're on 1.34.1, that might be a reason to move up :) Commented Oct 28, 2008 at 18:39

The thread library underwent a massive overhaul from 1.34 -> 1.35 so be aware of this when upgrading. Some breaking API changes were made, but they wont affect most people very much. The new version is more in line with the proposed C++0x library interface, which is a good thing in the long run.

Commented Oct 28, 2008 at 18:46

We don't use most of the threading library to heavily, but we do use it for our locks. I did bust into the details to specialize lock_ops in one place so I could use scoped_lock with my type. I agree that in the long run we want the more standard implementation.

Commented Oct 28, 2008 at 18:51

If you're lucky enough to have C++14 or C++17 available, you can also use shared_timed_mutex and a matching shared_lock .

Commented Oct 22, 2018 at 16:40

Using standard pre-tested, pre-built stuff is always good (for example, Boost as another answer suggested), but this is something that's not too hard to build yourself. Here's a dumb little implementation pulled out from a project of mine:

#include 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 not really being Windows-native, but the general idea is here. This implementation is slightly biased towards writers (a horde of writers can starve readers indefinitely); just modify writer_unlock if you'd rather the balance be the other way around.

Yes, this is C and not C++. Translation is an exercise left to the reader.

Edit

Greg Rogers pointed out that the POSIX standard does specify pthread_rwlock_* . This doesn't help if you don't have pthreads , but it stirred my mind into remembering: Pthreads-w32 should work! Instead of porting this code to non- pthreads for your own use, just use Pthreads-w32 on Windows, and native pthreads everywhere else.