What is thesync.RWMutex

First look at the definition of RWMutex

RWMutex is a read/write mutex. Locks can be held by any number of readers or by a single writer. The zero value of RWMutex is an unlocked mutex

This package contains four methods

func (rw *RWMutex) Lock         // Lock locks rw for writing.
func (rw *RWMutex) RLock        // RLock locks rw for reading.
func (rw *RWMutex) RUnlock      // Unlock locks rw for reading
func (rw *RWMutex) Unlock       // Unlock unlocks rw for writing
Copy the code

In this article, we will introduce the implementation of sync.RWMutex, again, with examples

var mu sync.RWMutex
var data map[string]string

func main(a) {
	data = map[string]string{"hoge": "fuga"}
	mu = sync.RWMutex{}
	go read()
	go read()
	go write()
	go read()
	time.Sleep(5 * time.Second)
}
/ / read method
func read(a) {
	println("read_start")
	mu.RLock()
	defer mu.RUnlock()
	time.Sleep(1*time.Second)
	println("read_complete", data["hoge"])}/ / write method
func write(a) {
	println("write_start")
	// mu.lock () Take a close look at these two lines of code, which are commented out here
	//defer mu.Unlock()
	time.Sleep(2 * time.Second)
	data["hoge"] = "piyo"
	println("write_complete")}Copy the code

What does it say if you comment out the above two lines?

read_start
read_start
write_start
read_start
read_complete fuga
read_complete fuga
read_complete fuga
write_complete
Copy the code

Does it matter if I open the code where I commented it out? The answer is yes!

Read_start write_start read_start read_start read_start read_complete fuga write_complete // You can see that read_complete has been replaced by the write string read_complete piyo read_complete piyoCopy the code

Sync. RWMutex lock is an RWMutex lock

type RWMutex struct {
	w           Mutex  // held if there are pending writers
	writerSem   uint32 // Semaphore (signal) for writers to wait for completing readers
	readerSem   uint32 // semaphore for readers to wait for completing writers
	readerCount int32  // number of pending readers
	readerWait  int32  // Number of departing readers
}
Copy the code

RLock & RUnlockRead lock & Release read lock

Set the maximum number of read locksconst rwmutexMaxReaders = 1 << 30RLock Locks the RW for reading. It should not be used for recursive read locking; Blocked lock calls exclude new reader access locks.func (rw *RWMutex) RLock(a) {

	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
	    // A writer is waiting...
		runtime_SemacquireMutex(&rw.readerSem, false)}// Static detection
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}
Copy the code

Where atom.addint32 (&rw.readerCount,1) performs the atom-plus operation, there are two scenarios:

  • No write lock at this time (readerCount> 0 + 1)RLockThe end of the normal
  • Write lock (readerCount+ 1) < 0 wait for write lock to end
RUnlock undoes a single RLock call; It does not affect other simultaneous readers. If the RW is not locked for reading when entering RUnlock, a runtime error occurs.func (rw *RWMutex) RUnlock(a) {

	if race.Enabled {
		_ = rw.w.state
		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
		race.Disable()
	}
	
	if r := atomic.AddInt32(&rw.readerCount, - 1); r < 0 {
		if r+1= =0 || r+1 == -rwmutexMaxReaders {
			race.Enable()
			// An error was reported to unlock an unlocked RWMutex
			throw("sync: RUnlock of unlocked RWMutex")}// If no reader is waiting
		if atomic.AddInt32(&rw.readerWait, - 1) = =0 {
			// The last reader unblocks the writer.
			runtime_Semrelease(&rw.writerSem, false)}}if race.Enabled {
		race.Enable()
	}
}
Copy the code
  • runtime_SemacquireMutexThe function isgoroutineWaiting queueenqueue
  • runtime_SemreleaseThe function isgoroutineWaiting queuedequeueoperation

Atom.addint32 (&rw.readerCount, -1) There are also several scenarios here (ps: only single read/write locks are considered here)

The lock Read locks & write locks Write lock Read lock
readerCount – (1 < < 30) panic 0
readerWait 0 panic 0

Lock & UnLockWrite lock & Release write lock

Lock Locks for writing. If the Lock is already locked for reading or writing, the Lock is locked until the Lock is available. func (rw *RWMutex)Lock() {// Static detectionifRace.enabled {_ = rw.w.state race.disable ()} // First, resolve the race problem with other writers (mutex is used here) rw.w.lock () // If a writer gets the lock,readerCount = - (1<<30), r = 0 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders // wait for active readers // r! =0 indicates a reader lock. Waitifr ! = 0 && atomic.AddInt32(&rw.readerWait, r) ! = 0 { runtime_SemacquireMutex(&rw.writerSem,false)}ifrace.Enabled { race.Enable() race.Acquire(unsafe.Pointer(&rw.readerSem)) race.Acquire(unsafe.Pointer(&rw.writerSem)) } }  func (rw *RWMutex)Unlock() {// Static detectionif race.Enabled {
		_ = rw.w.state
		race.Release(unsafe.Pointer(&rw.readerSem))
		race.Disable()
	}

	// Announce to readers there is no active writer.
	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
	if r >= rwmutexMaxReaders {
		race.Enable()
		throw("sync: Unlock of unlocked RWMutex"} // loop to wait for the reader to completefor i := 0; i < int(r); i++ {
		runtime_Semrelease(&rw.readerSem, false} // Allow other writers to proceed.(proceed) r.w.w.nlock ()if race.Enabled {
		race.Enable()
	}
}
Copy the code
The lock Read locks & write locks Write lock Read lock
readerCount 1 0 panic
readerWait 1 0 panic

that's all