This is the 28th day of my participation in the August Challenge

This article appears in my column:Let’s Golang

Golang: Common read/write lock in concurrent operations

The mutex is simple and crude. Read/write locks are slightly more complex than mutex locks, but I believe we can take them down today!

Read/write locks have two modes. That’s right! One is read mode, the other is write mode. When in write mode, it acts like a mutex, allowing only one coroutine to grab the lock and the others to queue up. But read mode is different, it allows you to read multiple coroutines, but not write. To sum it up:

  • Read – only mode: Multiple coroutines can be read but not written
  • Write only mode: single coroutine can be written but not read

On 32-bit operating systems, neither reading nor writing to int64 values can be done by a single CPU instruction. If a write operation executes its first instruction and then goes to another read coroutine, it will read the wrong data.

Here’s an example:

Let’s start with the main function:

func main(a) {
    for i:=0; i<5; i++{ wg06.Add(1)
        go write(i)
​
        wg06.Add(1)
        go read(i)
    }
    wg06.Wait()
}
Copy the code

Two coroutines are opened up at a time, one performing the write function and the other performing the read function. Then put it into the waiting group. Five times in total.

Let’s take a look at writing the function

func write(i int)  {
    // Lock to write-only mode, other coroutines are blocked
    rwm.Lock()
​
    fmt.Println(i,"writing...")
    <- time.After(10*time.Second)
    fmt.Println("write over!")
​
    rwm.Unlock()
    // Unlock write only mode
    wg06.Done()
}
Copy the code

This Lock() is the write mode that performs the read/write Lock. When this mode is in progress, only this coroutine can write and all other coroutines are blocked. Unlock() unlocks the lock-only mode, and the other coroutines in the waiting group are no longer blocked.

Take another look at the read mode:

func read(i int)  {
    rwm.RLock()
​
    fmt.Println(i,"reading...")
    <-time.After(10 * time.Second)
    fmt.Println(i,"read over!")
​
    rwm.RUnlock()
    wg06.Done()
}
Copy the code

RLock() is a read mode that performs a read/write lock, which other coroutines can read, but none can write.

If the program runs and the write coroutine grabs the lock first, all coroutines can’t be read, except this one, and everyone else is waiting. If the read coroutine grabbed the lock, it would be impossible to write the coroutine, but the read coroutine could still grab the lock.

Now do you know when we should use read/write locks?

When concurrent read/write operations are performed, read/write locks should be used for concurrent read/write operations when the number of reads far exceeds the number of writes.