30

Golang学习笔记之互斥锁(Mutex)

 5 years ago
source link: https://studygolang.com/articles/16933?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go语言包中的sync包提供了两种锁,互斥锁(sync.Mutex)和读写锁(sync.RWMutex)

这一篇博文我们只说一下互斥锁。

Mutex是一个互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。

• 它只有两个公开方法:Lock()加锁,unlock()解锁。

• 在同一个协程中加锁后,不能再继续对其加锁,否则会panic。只有在解锁之后才能再次加锁。

• 只允许只有一个读或者写的场景

• 在使用Unlock()解锁前,未使用Lock()加锁,就会引起一个运行错误。

函数原型

func (m *Mutex) Lock()

Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。

func (m *Mutex) Unlock()

Unlock方法解锁m,如果m未加锁会导致运行时错误。锁和线程无关,可以由不同的线程加锁和解锁。

一个例子理解互斥锁的作用

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)
        //开启协程
        go increment(&w)
    }
    w.Wait()
    fmt.Println("num =", num)
}

在上述程序中,调用1000个协程来进行num=num+1操作

运行几次的输出分别为

num = 971

num = 944

num = 959

每次运行都没有达到预期的效果,因为多个并发的协程试图访问 num 的值,这时就会发生竞态条件。

现在我们可以对上述程序加上锁,每次只能由一个线程来操作num的值

package main

import (
    "fmt"
    "sync"
)
var num = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
    //互斥锁
    m.Lock()     //当有线程进去进行加锁
    num = num + 1
    m.Unlock()   //出来后解锁,其他线程才可以进去
    wg.Done()
}
var w sync.WaitGroup
    var m sync.Mutex
    for i := 0; i < 1000; i++ {
        w.Add(1)
        go increment(&w, &m)//这里要传引用并不能传值,如果传值,那么每个协程都会得到 Mutex 的一份拷贝,竞态条件还是会发生。
    }
    w.Wait()
    fmt.Println("num =", num)

输出

num = 1000

我们也可以使用缓冲信道来实现互斥锁

func increment2(wg *sync.WaitGroup, b chan bool) {
    //自定义互斥锁
    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)
}

输出

num = 1000

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

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

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

    wa.Wait()
}

输出为

加锁0

上锁中0 加锁2 加锁3 加锁1 上锁中2

解锁0

解锁2 上锁中3 解锁3 上锁中1 解锁1


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK