Go语言的互斥锁Mutex

一、使用方法

Mutext是互斥锁的意思,也叫排他锁,同一时刻一段代码只能被一个线程运行,两个方法Lock(加锁)和Unlock(解锁)

package main

import (
    "fmt"
    "sync"
)

func main() {
    var count = 0
    var wg sync.WaitGroup
    //十个协程数量
    n := 10
    wg.Add(n)
    for i := 0; i < n; i++ {
        go func() {
            defer wg.Done()
            //1万叠加
            for j := 0; j < 10000; j++ {
                count++
            }
        }()
    }
    wg.Wait()
    fmt.Println(count)
}

运行结果如下

38532

添加锁

package main

import (
    "fmt"
    "sync"
)

func main() {
    var count = 0
    var wg sync.WaitGroup
    var mu sync.Mutex
    //十个协程数量
    n := 10
    wg.Add(n)
    for i := 0; i < n; i++ {
        go func() {
            defer wg.Done()
            //1万叠加
            for j := 0; j < 10000; j++ {
                mu.Lock()
                count++
                mu.Unlock()
            }
        }()
    }
    wg.Wait()
    fmt.Println(count)
}

运行结果如下,可以看到,已经看到结果变成了正确的100000

二、死锁场景

当两个或两个以上的进程在执行过程中,因争夺资源而处理一种互相等待的状态,如果没有外部干涉无法继续下去,这时我们称系统处于死锁或产生了死锁

1.Lock/Unlock不是成对出现

没有成对出现容易会出现死锁的情况,或者是Unlock 一个未加锁的Mutex而导致 panic,代码建议以下面紧凑的方式出现

mu.Lock()
defer mu.Unlock()

2.锁被拷贝使用

package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    mu.Lock()
    defer mu.Unlock()
    copyTest(mu)
}

//这里复制了一个锁,造成了死锁
func copyTest(mu sync.Mutex) {
    mu.Lock()
    defer mu.Unlock()
    fmt.Println("ok")
}

在函数外层已经加了一个Lock,在拷贝的时候又执行了一次Lock,因此这是一个永远不会获得的死锁

3.循环等待

A等待B,B等待C,C等待A,陷入了无限循环(哲学家就餐问题)

package main

import (
    "sync"
)

func main() {
    var muA, muB sync.Mutex
    var wg sync.WaitGroup

    wg.Add(2)
    go func() {
        defer wg.Done()
        muA.Lock()
        defer muA.Unlock()
        //A依赖B
        muB.Lock()
        defer muB.Lock()
    }()

    go func() {
        defer wg.Done()
        muB.Lock()
        defer muB.Lock()
        //B依赖A
        muA.Lock()
        defer muA.Unlock()
    }()
    wg.Wait()
}
Tags: