Mutex is used to ensure that only one goroutine accesses a shared resource. In a large number of concurrent scenarios, especially read scenarios, a shared resource block can only be accessed serially by a goroutine, which leads to performance impact, and the solution is to distinguish between read and write operations.
This turns a serial read into a parallel read, which is used to improve the performance of read operations.
Go standard library RWMutex (read/write lock) is used to solve the readers-writes problem.
The RWMutex in the standard library is a reader/writer mutex, RWMutex can only be held by n readers at a time, or by a single writer.
Lock/Unlock: method called on write operation, if held by reader or writer, Lock blocks until lock can be acquired, Unlock is to release lock.
Rlock/RUnlock: method to be called for read operation, if it is already held by writer, Rlock will keep blocking until the lock is obtained, otherwise it will return directly, RUlock is the method to release the lock by reader.
RLocker: return a Locker interface object for the read operation.
The zero value of RWMutex is the unlocked state, so there is no need for explicit initialization when using RWMutex as a variable or embedding it in a struct.
For readers-writers problem is based on the priority of read and write operations, the design of read-write locks is divided into three categories.
- Read-preferring read-priority design : good for concurrency, but can lead to write starvation in a large number of concurrent scenarios.
- Write-preferring write-preferring design : for new requests, mainly to avoid the writer starvation problem, that is, there is a reader and writer waiting to get the lock at the same time, will give priority to the writer.
- No priority specified: FIFO, does not distinguish between read and write priorities, and is suitable for some specific scenarios.
RWMutex design is write-preferring write-first design. A Lock call that is blocking will expel a new reader request to the lock.
RWMutex contains a Mutex, and four auxiliary fields writerSem, readerSem, readerCount, and readerWait.
Implementation of RLock/RUlock
Lock / Unlock
RWMutex is a multi-writer multi-reader lock, so there may be more than one writer and reader at the same time. then, to avoid competition between writers, RWMutex uses a Mutex to ensure mutual exclusion of writers. The RWMutex uses a Mutex to ensure mutual exclusion of writers to avoid competition between writers.
Once a writer has acquired an internal mutex lock, the readerCount field is inverted, changing it from a positive integer readerCount(>=0) to a negative number (readerCount-rwmutexMaxReaders) so that the field maintains two meanings (it holds the number of readers and indicates that there is currently a writer).
When a writer releases a lock, it reverses the readerCount field again. To be sure, since the current lock is held by the writer, the readerCount field is inverted and the constant rwmutexMaxReaders is subtracted to make it a negative number.
So, the inverse method here is to add the constant rwmutexMaxReaders to it. Now that the writer is releasing the lock, it’s time to wake up the new readers and let them continue their execution happily without blocking them.
Before the RWMutex’s Unlock returns, the internal mutex lock needs to be released. After it is released, other writers can continue to compete for the lock.
Three common mistakes made by RWMutex
- Deadlock due to reentry
- Releasing an unlocked RWMutex