Rwmutex: is a mutex-based read-write mutex, a goroutine can hold multiple read locks or a write lock, at the same time can only hold read lock or write lock
Data structure Design:
Type Rwmutex struct {
w mutex //Mutex
Writersem uint32// write lock semaphore
Readersem uint32/Read lock semaphore
readercount Int32// read lock counter
readerwait int32 //Get write lock to wait for the number of read lock releases
}
Gets the Write lock
func (rw *rwmutex) lock () {
if race. Enabled {
_ = rw.w.state
race. Disable ()
}
//First acquires a mutex
Rw.w.lock ()
//minus the maximum number of read locks, with 0-negative to indicate that the write lock has been obtained
r: = Atomic. AddInt32 (&rw.readercount,-rwmutexmaxreaders) + rwmutexmaxreaders
//sets the number of read locks to wait for release, and if so, suspends the acquisition of read locks Goroutine
if r! = 0 && Atomic. AddInt32 (&rw.readerwait, r)! = 0 {
//hang, monitor write lock semaphore
Runtime_semacquire (&rw.writersem)
}
if Race. Enabled {
race. Enable ()
race. Acquire (unsafe. Pointer (&rw.readersem))
race. Acquire (unsafe. Pointer (&rw.writersem))
}
}
In order this should introduce the code that releases the write lock, but because there are a few logical variables that are important in obtaining a write lock, it is strongly dependent on acquiring a read lock, so let's talk about the logic to get the read lock.
// Get write lock
func (rw * RWMutex) Lock () {
if race.Enabled {
_ = rw.w.state
race.Disable ()
}
// Get a mutex first
rw.w.Lock ()
// minus the maximum number of read locks, using 0-negative numbers to indicate that the write lock has been acquired
r: = atomic.AddInt32 (& rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Set the number of read locks waiting to be released, if any, suspend the goroutine that acquired the read lock
if r! = 0 && atomic.AddInt32 (& rw.readerWait, r)! = 0 {
// Suspend, monitor write lock semaphore
runtime_Semacquire (& rw.writerSem)
}
if race.Enabled {
race.Enable ()
race.Acquire (unsafe.Pointer (& rw.readerSem))
race.Acquire (unsafe.Pointer (& rw.writerSem))
}
}
// Obtain a read lock
func (rw * RWMutex) RLock () {
if race.Enabled {
_ = rw.w.state
race.Disable ()
}
// Every time a read lock is acquired, readerCount + 1
// If the write lock has been acquired, the readerCount is between -rwmutexMaxReaders and 0. At this time, the goroutine that acquired the read lock is suspended.
// If the write lock has not been acquired, then readerCount> = 0, then no more
// In this way, the positive and negative of readerCount becomes the judgment condition of the read lock and write lock mutual exclusion.
if atomic.AddInt32 (& rw.readerCount, 1) <0 {
// Hang, listen for readerSem semaphore
runtime_Semacquire (& rw.readerSem)
}
if race.Enabled {
race.Enable ()
race.Acquire (unsafe.Pointer (& rw.readerSem))
}
}
Func (rw *rwmutex) Unlock () {
if race. Enabled {
_ = rw.w.state
race. Release (unsafe. Pointer (&rw.readersem))
race. Release (unsafe. Pointer (&rw.writersem))
race. Disable ()
}
//Restore lock when the part Readercount
r: = Atomic. AddInt32 (&rw.readercount, rwmutexmaxreaders)
if R >= rwmutexmaxreaders {
race. Enable ()
Panic ("Sync:unlock of Unlocked Rwmutex")
}
//wake-up gets read lock all blocked Goroutine for
i: = 0; i < in T (r); i++ {
runtime_semrelease (&rw.readersem)
}
//Releases the mutex resource
rw.w.unlock ()
if race. Enabled {
race. Enable ()
}
}
Summarize:
The implementation of the read-write mutex is more skillful and requires several
1. Read lock can not block read lock, introduce Readercount implementation
2. The read lock needs to block the write lock until the read lock is released, introducing the READERSEM implementation
3. The write lock needs to block the read lock until the write lock is released, introducing the WIRTERSEM implementation
4. Write lock need to block write lock, introduce METUX implementation