How can I implement a C++ Reader-Writer lock using a single unlock method, which can be called be a reader or writer?
Well, define two functions UnlockRead
and UnlockWrite
.
I believe you do not need both accesses (Write/Read) at the same time in the same place. So what I am proposing is to have two other classes for locking access:
class ReadWriteAccess
{
public:
ReadWriteAccess(uint32_t maxReaders);
~ReadWriteAccess();
uint32_t GetMaxReaders() const;
uint32_t GetMaxReaders() const;
eResult GetReadLock(int32_t timeout);
eResult GetWriteLock(int32_t timeout);
eResult UnlockWrite();
eResult UnlockRead();
private:
uint32_t m_MaxReaders;
Mutex* m_WriterMutex;
Semaphore* m_ReaderSemaphore;
};
And have separate classes for read and write lock and use RAII to be always on safe side:
class ReadLock
{
public:
ReadLock(ReadWriteAccess& access, int32_t timeout) : access(access)
{
result = access.GetReadLock(timeout);
}
eResult getResult() const { return result; }
~ReadLock()
{
if (result)
access.UnlockRead();
}
private:
ReadWriteAccess& access;
eResult result;
};
and use like this:
T someResource;
ReadWriteAccess someResourceGuard;
void someFunction()
{
ReadLock lock(someResourceGuard);
if (lock.getResult())
cout << someResource; // it is safe to read something from resource
}
Of course, the very similar implementation you can easily write by yourself for WriteLock
Since OP insisted in comments to have "one" Unlock - please consider the drawbacks:
Assume it is implemented some kind of stack of last calls to Lock functions:
class ReadWriteLock
{
public:
ReadWriteLock(uint32_t maxReaders);
~ReadWriteLock();
uint32_t GetMaxReaders() const;
eResult GetReadLock(int32_t timeout)
{
eResult result = GetReadLockImpl(timestamp);
if (result)
lockStack.push(READ);
}
eResult GetWriteLock(int32_t timeout)
{
eResult result = GetWriteLockImpl(timestamp);
if (result)
lockStack.push(WRITE);
}
eResult Unlock()
{
LastLockMode lockMode = lockStack.top();
lockStack.pop();
if (lockMode == READ)
UnlockReadImpl();
else
UnlockWriteImpl();
}
private:
uint32_t m_MaxReaders;
Mutex* m_WriterMutex;
Semaphore* m_ReaderSemaphore;
enum Mode { READ, WRITE };
std::stack<Mode> lockStack;
};
But the above would work only in one-thread application. And one-thread application never need any locks.
So - you have to have multi-thread stack - like:
template <typename Value>
class MultiThreadStack
{
public:
void push(Value)
{
stackPerThread[getThreadId()].push(value);
}
Value top()
{
return stackPerThread[getThreadId()].top();
}
void pop()
{
stackPerThread[getThreadId()].pop();
}
private:
ThreadId getThreadId() { return /* your system way to get thread id*/; }
std::map<ThreadId, std::stack<Value>> stackPerThread;
};
So use this MultiThreadStack
not std::stack in ReadWriteLock
.
But, the std::map
above would need ReadWriteLock
to lock access to it from multuple threads - so, well, either you know all your threads before you start using this stuff (preregistration) or you end up in the same problem as described here. So my advice - if you can - change your design.
A RW lock for c++11 threads
std::shared_mutex
will be part of the C++14 Standard Library. It did not make it to C++11 just because there was no time to formulate a proposal and discuss it thoroughly.
You can still use boost::shared_mutex
though. Under Windows, if you are working with Windows Vista or later, you can use Slim Read-Write Locks, which are optimized for speed and memory consumption.
A readers/writer lock... without having a lock for the readers?
What you're describing is very similar to double instance locking and left-right concurrency control.
In terms of progress guarantees, the difference between the two is that the former is lock-free for readers while the latter is wait-free. Both are blocking for writers.
How to implement a reader/writer lock in C++14
C++14 has the read/writer lock implementation std::shared_timed_mutex
.
Side-note: C++17 added the simpler class std::shared_mutex
, which you can use if you don't need the extra timing functions (like shared_timed_mutex::try_lock_for
and shared_timed_mutex::try_lock_until
).
However, before using a read/writer lock, be aware of the potentially harmful performance implications. Depending on the situation, a simple std::mutex
might be faster.
optimize the implementation of read/write locks
- whether I should use notify_all when releasing the lock? According to the document, once a thread gets notified, it will reacquire the lock. If using notify_all, then multiple threads can reacquire the same lock. What will happen then? And whether will a thread acquire the lock before checking the condition (i.e., lock_!=NO_LOCK && lock_!=READ_LOCK)?
Yes, you should use notify_all when releasing the lock. All condition_ waiting for the mutex_ will be waked one by one for they must lock the mutex_ firstly(this is done inner the condition_ wait operation).
- how can I optimize the program? obviously, when releasing a read lock, we only need to notify the threads that attempt to acquire the write lock, since read never blocks read. So how to implement this idea?
All threads waiting for mutex_ must be notified, for some writing threads may waiting for the read lock to be released.
I hope this will help you!
Reader/Writer Locks in 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
.
Related Topics
Using Qsocketnotifier to Select on a Char Device
How to Compile SQLite with Icu
Does Vector::Erase() on a Vector of Object Pointers Destroy the Object Itself
How to Access the Underlying Container of Stl Container Adaptors
How to Know If One Shared Library Depends on Another Shared Library or Not
How to Make a Function Async-Signal-Safe
Libstdc++ Static Linking in Dynamic Library
/Usr/Lib64/Libstdc++.So.6: Version 'Glibcxx_3.4.15' Not Found
How to Get a List of Installed True Type Fonts on Linux Using C or C++
Pthread Condition Variables Not Signalling Even Though Set to Pthread_Process_Shared
Shgetknownfolderpath Equivalent API in Linux
Hello World Python Extension in C++ Using Boost
Detecting If Computer Is Idle Based on Mouse and Keyboard Interactions
How to Convert Std::Chrono::Time_Point to Calendar Datetime String with Fractional Seconds
When Do We Have to Use Copy Constructors
Extract Year/Month/Day etc. from Std::Chrono::Time_Point in C++