生产者 - 消费者模型实现
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
// 生产者
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()
// 消费者
go func() {
for val := range ch {
fmt.Println("Consumed:", val)
}
}()
// 防止主函数退出
select {}
}
避免数据竞争
- 使用通道(Channel):在上述代码中,通过使用
chan
来传递数据。通道是类型化的管道,通过它可以在多个 goroutine 之间进行安全的数据传递。由于通道本身是线程安全的,通过通道传递数据就避免了对共享数据的直接访问,从而避免了数据竞争。
- 避免共享可变数据:尽量减少不同 goroutine 之间对共享可变数据的操作。在生产者 - 消费者模型中,数据通过通道进行传递,而不是多个 goroutine 直接读写共享变量。
避免死锁
- 正确关闭通道:在生产者完成数据生产后,要及时关闭通道。如上述代码中,生产者在生产完数据后调用
close(ch)
。消费者通过 for... range
循环从通道读取数据,当通道关闭时,循环会自动结束,避免了消费者因一直等待数据而导致的死锁。
- 确保缓冲足够:如果使用带缓冲的通道,要确保缓冲大小足够,防止生产者因通道缓冲区满而阻塞,同时消费者又在等待数据的情况。例如,如果预计生产者会快速生产大量数据,可以适当增大通道的缓冲大小
ch := make(chan int, 10)
。
- 避免循环依赖:在更复杂的并发场景中,要注意避免出现 goroutine 之间相互等待对方释放资源的循环依赖情况,确保各个 goroutine 的执行逻辑不会形成死锁的条件。