在 Golang 的并发模型中,由于多个 goroutine 同时操作共享资源,容易出现一些并发错误。
常见的并发错误有:
- 竞争条件(Race Condition):多个 goroutine 同时访问某个资源,导致最终结果出错。
例如:
var counter = 0
func inc() {
counter++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
``` func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
inc()
}
}()
}
wg.Wait()
println(counter) // 最终结果是未知的
}
这里多个 goroutine 同时调用 inc(),从而导致 counter 最终的值是未知的。这个就是典型的竞争条件错误。
- 死锁(Deadlock):两个或多个 goroutine 相互等待对方占有的资源,导致永久阻塞。
例如:
var lock1 = sync.Mutex{}
var lock2 = sync.Mutex{}
func foo() {
lock1.Lock()
lock2.Lock() // 锁定lock2
// ...
lock2.Unlock()
lock1.Unlock()
}
func bar() {
lock2.Lock()
lock1.Lock() // 锁定lock1
// ...
lock1.Unlock()
lock2.Unlock()
}
func main() {
go foo()
go bar()
}
foo 和 bar goroutine 相互等待对方锁定的资源,最终导致程序无限阻塞。
- 洪水泛滥(Flooding):一个或多个快速执行的 goroutine 产生大量的请求或数据,拖慢其他 goroutine 的执行速度。
例如:
func doWork(jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
go doWork(jobs, results)
for i := 0; i < 10; i++ {
go func() {
for {
jobs <- 1
}
}()
}
}
这里 10 个 goroutine 快速发送 job,最终会导致 doWork goroutine 来不及处理,产生大量积压请求。这就是典型的洪水泛滥错误。
所以并发编程中,我们最需要注意的是竞争条件和死锁等并发错误。通过互斥锁、通信与同步等手段可以尽量避免这些错误的发生。