Mutex of learning notes in Golang

Keywords: Go

The sync package in the Go language pack provides two types of locks, mutually exclusive lock (sync.Mutex) and read-write lock (sync.RWMutex)
We only talk about mutex in this blog post.

Mutex is a mutually exclusive lock that can be created as a field of other structures; zero value is the unlocked state. Mutex type locks are thread independent and can be locked and unlocked by different threads.

• it has only two public methods: Lock() to lock and unlock().
After locking in the same process, it can't be locked again, otherwise it will panic. It can only be locked again after unlocking.
• only one read or write scenario is allowed
• before unlocking with Unlock(), locking without Lock() will cause a running error.

Function prototype
func (m *Mutex) Lock()
The Lock method locks M. if M is already locked, it blocks until m is unlocked.
func (m *Mutex) Unlock()
The Unlock method unlocks m. if m is unlocked, it will cause a runtime error. Locks are independent of threads, and can be locked and unlocked by different threads.
An example to understand the function of mutex

package main
import (
    "fmt"
    "sync"
)
var num = 0
func increment(wg *sync.WaitGroup) {
    num = num + 1
    wg.Done()
}
func main() {
    var w sync.WaitGroup
    for i := 0; i < 1000; i++ {
        w.Add(1)
        //Open Association
        go increment(&w)
    }
    w.Wait()
    fmt.Println("num =", num)
}

In the above program, 1000 processes are called to perform num=num+1 operation
The output of several runs is
num = 971
num = 944
num = 959
Each run does not achieve the expected effect, because multiple concurrent processes try to access the value of num, then race conditions will occur.
Now we can lock the above program. Only one thread can operate num value at a time

package main

import (
    "fmt"
    "sync"
)
var num = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
    //mutex
    m.Lock()     //When a thread enters for locking
    num = num + 1
    m.Unlock()   //Unlock after coming out, other threads can enter
    wg.Done()
}
var w sync.WaitGroup
    var m sync.Mutex
    for i := 0; i < 1000; i++ {
        w.Add(1)
        go increment(&w, &m)//If you want to transfer a reference, you cannot transfer a value. If you transfer a value, each process will get a copy of Mutex. The race condition will still occur.
    }
    w.Wait()
    fmt.Println("num =", num)

output
num = 1000
We can also use buffered channels to implement mutexes

func increment2(wg *sync.WaitGroup, b chan bool) {
    //Custom mutex
    b <- true
    num = num + 1
    <-b
    wg.Done()
}
func main() {
    var w sync.WaitGroup
    ch := make(chan bool,1)
    for i := 0; i < 1000; i++ {
        w.Add(1)
        go increment2(&w, ch)
    }
    w.Wait()
    fmt.Println("num =", num)
}

output
num = 1000

func main() {
    wa := sync.WaitGroup{}

    var mu sync.Mutex
    fmt.Println("Lock 0")
    mu.Lock()

    fmt.Printf("Lock 0\t")
    for i := 1; i < 4; i++ {
        wa.Add(1)
        go func(i int) {
            fmt.Printf("Lock up%d\t", i)
            mu.Lock()
            fmt.Printf("Locked in%d\t", i)
            time.Sleep(time.Second * 1)
            mu.Unlock()
            fmt.Printf("Unlock%d\t", i)
            wa.Done()
        }(i)
    }
    time.Sleep(time.Second * 5)
    mu.Unlock()
    fmt.Println("\n Unlock 0")

    wa.Wait()
}

Output is
Lock 0
Lock in 0, lock in 2, lock in 3, lock in 1, lock in 2
Unlock 0
Unlocking 2, locking 3, unlocking 3, locking 1, unlocking 1

Posted by keithschm on Mon, 02 Dec 2019 15:31:53 -0800