Read-writers lock — Accessing shared resources
In multithreaded programming, when accessing a shared resource, a mutex is used to ensure that not more than one thread is allowed to change resources simultaneously. A mutex guarantees safety because it restricts the number of threads accessing a shared resource to one, but in some cases, it can be inefficient. For instance, what if several threads need to access a shared resource at the same time, but only some of them change values? In this case, a thread that only reads values can be allowed simultaneous access. However, if a thread is changing the value, no other thread should access the shared resource. Conversely, while another thread is accessing a shared resource, the thread that wants to change a value should not access it.
What is used in situations like this is known as a readers-writer lock, also known as a shared-exclusive lock. A readers-writer lock allows multiple readers and a single writer. It is also called a multiple-readers/single-writer lock(MRSW lock). In other words, if it acquires a read lock on a readers-writer lock that already acquired a read lock, it can immediately acquire a lock on it and execute the following code. However, if it acquires a write lock, a lock cannot be acquired and it must wait until the read lock is released.
If that’s the case, what happens if thread A acquires a read lock and thread B acquires a write lock and thread C acquires a read lock? It’s possible for one to think that thread A has acquired the read lock, so the write lock on thread B cannot be acquired and must wait, but the read lock on thread C can be acquired, and thus, threads A and C will be executed. However, in this implementation, if read locks are acquired frequently, then the write lock may not be executed indefinitely. This phenomenon is called a write-starvation.
Thus, some may implement a readers-write lock in a way that it waits for a write lock to be acquired without waiting for a new read lock if a write lock is waiting, even if a read lock is already acquired and a read lock can be acquired. In this case, the efficiency is lowered because the read lock is unnecessarily waited for and not acquired. However, the lock that started waiting earlier gets acquired first, and thus, the fairness factor rises. A write-starvation can occur, but an implementation that can effectively acquire a read lock is called read-preferring. Conversely, a slightly less efficient implementation is called write-preferring. Which of these is better depends on the situation. A write-preferring lock is a good idea if you can get a write-lock starvation due to frequent read locks. However, if you are not worried about write-starvation, the read-preferring implementation is lighter, and you can once again acquire a read lock on the same thread for MRSW locks that already acquired a read lock.
Some implementations specify that they use one of these policies; however, some implementations do not specify which policy is used. If a policy is not explicitly specified, usually, it is to leave room for future optimizations. In that case, you should write code that is suitable for both read-preferring or write-preferring.
Original Korean post: read-writers lock — 공유 자원 접근하기
Translated by Seung Woo Kim