SoFunction
Updated on 2025-03-03

golang RWMutex read and write lock to realize read-shared write exclusive function example

introduction

In previous articlegolang Important knowledge: mutexIn this article, we introduce the relevant principles of the mutex mutex implementation. In addition to mutex locks, there is also the read-write lock RWMutex in Go, which is mainly used to realize the functions of read-sharing and write exclusiveness. Today we will also analyze the read and write locks to deepen our understanding of Go locks

The principle of the implementation of read and write lock

The so-called read and write lock actually controls the synchronous mutual exclusion between Goroutines for the following two scenarios:

  • Multiple goroutines occupy read locks together, and they do not affect each other. You can continue with the logical code behind them.
  • If the write lock is possessed, the subsequent goroutine will be blocked and waited until the current write lock is released.

After understanding the above scenario requirements, the implementation is much simpler. The key is to judge whether it is currently in the write lock state. After all, there is a blocking waiting action.

According to conventional ideas, we generally use an identification bit to maintain this state. However, Go official saved even this step.

It uses a number of read locks that are already maintained, and when writing locks are occupied, it becomes a negative number.

There are new read and write operations later. You only need to determine whether the value is positive or negative. A negative number means that the write lock is currently in use and needs to be blocked and waited.

After the write lock possession is completed, the value will return to a positive number, and a new read and write operation can be performed.

RWMutex source code analysis

Next, we'llsrc/runtime/The code structure of RWMutex is analyzed in detail.

// rwmutex is a read-write mutually exclusive lock// Multiple goroutines will be allowed to hold read locks, but only one write lock will hold// rwmutex used to assist in writing lock mutextype rwmutex struct {
rLock      mutex    // Used to protect the settings of readers, readerPass, writerreaders    muintptr // The goroutine read lock queue that is waiting for dormant will be called up after the write lock possession is completed.readerPass uint32   // The number of goroutines that need to be skipped in the read lock queue. When the write lock is over, the goroutine in the read lock queue will be called, but some may no longer be in the queue, and this part needs to be skipped.wLock  mutex    // Used for mutex between writerswriter muintptr // writer waiting for the reading to be completedreaderCount uint32 // Number of goroutines that are performing a read operationreaderWait  uint32 // The number of waiting for the read lock to be released.  When the write lock is occupied, there are still some of the read locks in front of it, and they need to be released before they can continue.}

RWMutex's Lock() Analysis

func (rw *rwmutex) Lock() {
    // Used for competition between multiple write locks    lock(&)
    m := getg().m
    // Set readerCount to a negative number to determine whether it is currently in the write lock possession state.    // < 0 means that the write lock is currently in possession.    r := int32((&amp;, -rwmutexMaxReaders)) + rwmutexMaxReaders
    // There is a reading lock in front of it, you need to wait until it is released before continuing    lock(&amp;)
    if r != 0 &amp;&amp; (&amp;, r) != 0 {
        systemstack(func() {
            (m)
            unlock(&amp;)
            notesleep(&amp;)
            noteclear(&amp;)
        })
    } else {
        unlock(&amp;)
    }
}

RLock() analysis of RWMutex

func (rw *rwmutex) Rlock() {
    acquirem()
    if int32((&amp;, 1)) &lt; 0 {
        // The number of read locks readerCount + 1 is less than 0, indicating that it is currently occupied by the write lock.        // Wait for the write lock to be released        systemstack(func() {
            lock(&amp;)
            if  &gt; 0 {
                 -= 1
                unlock(&amp;)
            } else {
                // Wait for the write lock to be called                m := getg().m
                 = 
                (m)
                unlock(&amp;)
                notesleep(&amp;)
                noteclear(&amp;)
            }
        })
    }
}

Unlock() analysis of RWMutex

func (rw *rwmutex) Unlock() {
    // Restore the readerCount that was originally written to a negative number.    r := int32((&amp;, rwmutexMaxReaders))
    if r &gt;= rwmutexMaxReaders {
        throw("unlock of unlocked rwmutex")
    }
    // Evoke the read lock you were waiting for before.    lock(&amp;)
    for () != nil {
        reader := ()
         = 
        (nil)
        notewakeup(&amp;)
        r -= 1
    }
    // If r > 0, it means that the goroutine in the read lock queue is no longer in the queue, and this part needs to be skipped     += uint32(r)
    unlock(&amp;)
    // Unlock the write lock    unlock(&amp;)
}

RUnlock() analysis of RWMutex

func (rw *rwmutex) RUnlock() {
    // If readerCount < 0 after release, it means that the current write lock is occupying    if r := int32((&amp;, -1)); r &lt; 0 {
        if r+1 == 0 || r+1 == -rwmutexMaxReaders {
            throw("runlock of unlocked rwmutex")
        }
        // readerWait == 0, which means that the previous read lock has been released.        // Need to call the write lock        if (&amp;, -1) == 0 {
            // The last reader unblocks the writer.
            lock(&amp;)
            w := ()
            if w != nil {
                notewakeup(&amp;)
            }
            unlock(&amp;)
        }
    }
    releasem(getg().m)
}

Summarize

RWMutex determines whether it is currently in read lock possession or write lock possession by the positive and negative readerCount.

After being in the write lock possession state, the readerCount at this time will be assigned to readerWait, indicating that you have to wait until the previous readerWait read lock is released before you can be considered a complete possession write lock before you can perform the subsequent exclusive operation.

When the read lock is released, the readerWait will be decremented by one until it is 0, and the write lock can be called.

And after the write lock is occupied, a new read operation will be added immediately, which will not affect the readerWait value, but will only affect the total number of read locks: readerCount.

The above is the detailed content of the golang RWMutex read and write lock to implement read-share and write exclusive function examples. For more information about golang RWMutex read-write lock, please follow my other related articles!