标签导航:

go语言互斥锁:外层锁是否会影响内层锁的获取?

深入Go语言互斥锁的并发行为

本文分析Go语言sync.Mutex互斥锁的并发行为,重点探讨外层锁对内层锁获取的影响。以下代码片段展示了一个常见的场景:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var mutex sync.Mutex
    wait := sync.WaitGroup{}

    fmt.Println("Locked")
    mutex.Lock() // 外层锁

    for i := 1; i <= 3; i++ {
        wait.Add(1)
        go func(i int) {
            defer wait.Done()
            fmt.Printf("Not locked: %d
", i)
            mutex.Lock()
            fmt.Printf("Locked: %d
", i)
            time.Sleep(100 * time.Millisecond)
            mutex.Unlock()
            fmt.Printf("Unlocked: %d
", i)
        }(i)
    }

    fmt.Println("Unlocked")
    mutex.Unlock() // 释放外层锁
    wait.Wait()
}

这段代码中,主goroutine先获取mutex锁,然后启动三个goroutine,每个goroutine都尝试获取同一个锁。 很多人误以为外层锁会完全阻止内层锁的获取。

然而,实际运行结果并非如此:

Locked
Not locked: 1
Not locked: 2
Not locked: 3
Unlocked
Locked: 1
Unlocked: 1
Locked: 2
Unlocked: 2
Locked: 3
Unlocked: 3

结果表明,三个goroutine在尝试获取锁(mutex.Lock())之前,已经执行了fmt.Printf("Not locked: %d ", i)语句。 Unlocked的打印位置也证实了这一点:它在所有goroutine尝试获取锁但尚未成功之前打印。 之后,goroutine才依次获取锁,执行后续代码,并最终释放锁。

因此,外层锁并非没有影响内层锁,而是Go语言的并发调度机制导致goroutine在尝试获取锁前已执行部分代码。 外层锁的持有阻止了goroutine立即获取内层锁,但并不阻止goroutine执行其他代码。 只有在外层锁释放后,等待的goroutine才能依次获得锁。