美文网首页程序员区块链研习社Golang
Golang学习笔记之互斥锁(Mutex)

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

作者: 学生黄哲 | 来源:发表于2018-12-13 20:38 被阅读16次
    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

    相关文章

      网友评论

      • Mr丶易客:作为你的忠实小迷妹,每天晚上八点半左右准时等待你的更新,文章有营养,很务实

      本文标题:Golang学习笔记之互斥锁(Mutex)

      本文链接:https://www.haomeiwen.com/subject/rlobhqtx.html