面试题答案
一键面试生产者 - 消费者模型实现
在Go语言中,使用Go协程和通道可以很方便地实现生产者 - 消费者模型。以下是一个简单的代码示例:
package main
import (
"fmt"
)
func producer(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
fmt.Printf("Produced: %d\n", i)
}
close(ch)
}
func consumer(ch chan int) {
for num := range ch {
fmt.Printf("Consumed: %d\n", num)
}
}
func main() {
ch := make(chan int)
go producer(ch)
go consumer(ch)
// 防止主函数提前退出
select {}
}
通道的作用
- 数据传递:通道用于在生产者和消费者之间传递数据。生产者将数据发送到通道,消费者从通道接收数据,实现了数据的流动。
- 同步:通道还起到同步生产者和消费者的作用。当通道已满(对于有缓冲通道)或者没有缓冲区且没有接收者时,生产者发送数据会阻塞;当通道为空(对于有缓冲通道)或者没有缓冲区且没有发送者时,消费者接收数据会阻塞。这种机制保证了生产者和消费者之间的协调工作。
处理通道关闭
- 生产者关闭通道:在生产者完成数据生产后,应该关闭通道。如上述代码中,
producer
函数在生产完数据后调用close(ch)
关闭通道。这是告诉消费者不会再有新的数据到来。 - 消费者检测通道关闭:消费者可以使用
for... range
循环来接收通道数据,当通道关闭时,for... range
会自动退出。如consumer
函数中的for num := range ch
。另外,也可以使用ok
-idiom来检测通道关闭,例如num, ok := <-ch
,当ok
为false
时表示通道已关闭且无数据。
处理数据竞争问题
- 使用通道同步:Go语言中通过通道进行数据传递本身就避免了大部分的数据竞争问题。因为通道操作(发送和接收)是线程安全的,多个协程通过通道交互数据不会产生数据竞争。
- 避免共享可变状态:尽量减少协程之间共享可变数据。如果必须共享,使用
sync
包中的工具(如sync.Mutex
)来保护共享数据,防止多个协程同时访问导致数据竞争。在生产者 - 消费者模型中,由于数据通过通道传递,不需要共享可变数据,所以通常不会出现数据竞争问题。