
深入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才能依次获得锁。

