在您提供的代码中,死锁问题主要源于sync.WaitGroup的错误使用方式。以下是具体分析和修复方案:
问题分析:
defer wg.Done()被错误地放置在main函数中,而不是在goroutine内部执行
wg.Add(1)在goroutine启动后才调用,存在竞态条件
- goroutine完成后没有调用
wg.Done()来通知WaitGroup
修正后的代码:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
worker := func() {
defer wg.Done() // 确保在goroutine结束时调用
for i := 0; i < 100000; i++ {
fmt.Printf("%v\n", i)
}
}
wg.Add(1) // 在启动goroutine前先增加计数器
go worker()
wg.Wait() // 等待所有goroutine完成
}
关键修复点:
- 正确的WaitGroup使用顺序:
wg.Add(1) // 先增加计数器
go worker() // 再启动goroutine
- 在goroutine内部调用Done:
defer wg.Done() // 使用defer确保函数返回时一定会执行
避免死锁的最佳实践:
- 确保
wg.Add()在启动goroutine之前调用
- 使用
defer wg.Done()来保证即使发生panic也能正确释放计数器
- 避免在多个goroutine中并发调用
wg.Add()
- WaitGroup的计数器不能为负数
另一个常见死锁场景的示例:
// 错误的锁使用方式
var mu sync.Mutex
func deadlockExample() {
mu.Lock()
// 某些操作...
mu.Lock() // 第二次加锁会导致死锁
mu.Unlock()
mu.Unlock()
}
// 正确的做法:使用sync.RWMutex
var rwmu sync.RWMutex
func correctExample() {
rwmu.RLock()
// 读操作...
rwmu.RUnlock()
rwmu.Lock()
// 写操作...
rwmu.Unlock()
}
通过遵循这些WaitGroup的正确使用模式,可以有效避免代码中的死锁问题。