Reader/Writer Locks in C++

C++MultithreadingLocking

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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

#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
}

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

#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;

Solution 2 - C++

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.

Solution 3 - C++

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 <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 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: http://sourceware.org/pthreads-win32/">Pthreads-w32</a> 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.

Solution 4 - C++

Whatever you decide to use, benchmark your work load against simple locks, as read/write locks tend to be 3-40x slower than simple mutex, when there is no contention.

Here is some reference

Solution 5 - C++

Edit: The MSDN Magazine link isn't available anymore. The CodeProject article is now available on https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks and sums it up pretty nicely. Also I found a new MSDN link about Compound Synchronisation Objects.

There is an article about reader-writer locks on MSDN that presents some implementations of them. It also introduces the Slim reader/writer lock, a kernel synchronisation primitive introduced with Vista. There's also a CodeProject article about comparing different implementations (including the MSDN article's ones).

Solution 6 - C++

C++17 supports std::shared_mutex . It is supported in MSVC++ 2015 and 2017.

Solution 7 - C++

Intel Thread Building Blocks also provide a couple of rw_lock variants:

http://www.threadingbuildingblocks.org/

They have a spin_rw_mutex for very short periods of contention and a queueing_rw_mutex for longer periods of contention. The former can be used in particularly performance sensitive code. The latter is more comparable in performance to that provided by Boost.Thread or directly using pthreads. But profile to make sure which one is a win for your access patterns.

Solution 8 - C++

I can recommend the ACE library, which provides a multitude of locking mechanisms and is ported to various platforms.

Depending on the boundary conditions of your problem, you may find the following classes useful:

  • ACE_RW_Process_Mutex
  • ACE_Write_Guard and ACE_Read_Guard
  • ACE_Condition

Solution 9 - C++

Boost.Thread has since release 1.35.0 already supports reader-writer locks. The good thing about this is that the implementation is greatly cross-platform, peer-reviewed, and is actually a reference implementation for the upcoming C++0x standard.

Solution 10 - C++

http://www.codeproject.com/KB/threads/ReaderWriterLock.aspx

Here is a good and lightweight implementation suitable for most tasks.

Solution 11 - C++

Multiple-Reader, Single-Writer Synchronization Lock Class for Win32 by Glenn Slayde

http://www.glennslayden.com/code/win32/reader-writer-lock

Solution 12 - C++

You could copy Sun's excellent ReentrantReadWriteLock. It includes features such as optional fairness, lock downgrading, and of course reentrancy.

Yes it's in Java, but you can easily read and transpose it to C++, even if you don't know any Java. The documentation I linked to contains all the behavioral properties of this implementation so you can make sure it does what you want.

If nothing else, it's a guide.

Solution 13 - C++

#include <shared_mutex>

class Foo {
 public:
  void Write() {
    std::unique_lock lock{mutex_};
    // ... 
  }

  void Read() {
    std::shared_lock lock{mutex_};
    // ... 
  }

 private:
  std::shared_mutex mutex_;
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatt PriceView Question on Stackoverflow
Solution 1 - C++Yochai TimmerView Answer on Stackoverflow
Solution 2 - C++Greg RogersView Answer on Stackoverflow
Solution 3 - C++ephemientView Answer on Stackoverflow
Solution 4 - C++ididakView Answer on Stackoverflow
Solution 5 - C++vividosView Answer on Stackoverflow
Solution 6 - C++Serge RogatchView Answer on Stackoverflow
Solution 7 - C++Edward KmettView Answer on Stackoverflow
Solution 8 - C++Dong HoonView Answer on Stackoverflow
Solution 9 - C++Dean MichaelView Answer on Stackoverflow
Solution 10 - C++Vladislav RastrusnyView Answer on Stackoverflow
Solution 11 - C++KindDragonView Answer on Stackoverflow
Solution 12 - C++Jason CohenView Answer on Stackoverflow
Solution 13 - C++FakerView Answer on Stackoverflow