package main
import (
"fmt"
)
func worker(id int, resultChan chan int, doneChan chan struct{}) {
// 模拟任务执行
var res int
for i := 0; i < 10; i++ {
res += i
}
resultChan <- res
doneChan <- struct{}{}
}
func main() {
numWorkers := 5
resultChan := make(chan int)
doneChan := make(chan struct{}, numWorkers)
for i := 0; i < numWorkers; i++ {
go worker(i, resultChan, doneChan)
}
var totalResult int
for i := 0; i < numWorkers; i++ {
totalResult += <-resultChan
<-doneChan
}
close(resultChan)
close(doneChan)
fmt.Printf("汇总结果: %d\n", totalResult)
}
避免死锁
- 缓冲区设置:在创建
doneChan
时,设置了缓冲区大小为numWorkers
。这样可以避免在worker
还未向doneChan
发送数据时,main
函数就开始接收,从而导致死锁。
- 接收与关闭顺序:在
main
函数中,先接收resultChan
中的结果,再接收doneChan
中的信号,确保每个worker
都有机会发送完成信号。同时,在所有数据接收完毕后,关闭resultChan
和doneChan
,防止向已关闭的通道发送数据造成死锁。
处理竞争条件
- 通道同步:通过使用通道(
resultChan
和doneChan
)来进行数据传递和同步,Goroutine之间的通信是线程安全的,避免了传统共享内存带来的竞争条件。每个worker
将结果发送到resultChan
,主Goroutine
从resultChan
接收,这一过程是有序且安全的。
- 无共享数据:在
worker
函数中,每个worker
都有自己独立的res
变量,不存在多个Goroutine
同时读写同一变量的情况,进一步避免了竞争条件。