面试题答案
一键面试无缓冲的chan
- 内存管理特点:无缓冲的chan在发送和接收操作时,会导致 goroutine 阻塞,直到对应的接收或发送操作准备好。它不持有任何数据缓冲,数据直接从发送者传递到接收者,所以内存占用相对稳定,不会因为缓冲数据而额外占用内存。
- 举例及场景选择:例如实现生产者 - 消费者模式且需要严格同步时。
package main
import (
"fmt"
)
func producer(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
fmt.Println("Produced:", i)
}
close(ch)
}
func consumer(ch chan int) {
for val := range ch {
fmt.Println("Consumed:", val)
}
}
func main() {
ch := make(chan int)
go producer(ch)
go consumer(ch)
select {}
}
在此场景中,生产者每生产一个数据就立即传递给消费者,保证两者紧密同步,适合对数据传递实时性要求高且数据处理速度匹配的场景。
有缓冲的chan
- 内存管理特点:有缓冲的chan允许在缓冲区满之前,发送操作不会阻塞。缓冲区的大小决定了它能暂存数据的数量,这意味着在一定程度上会额外占用内存来存储这些缓冲的数据。如果缓冲区一直不满,发送操作可以持续进行而不阻塞,从而提高并发效率。
- 举例及场景选择:比如在处理大量日志记录时,生产者可能生成日志的速度较快,而消费者处理日志存储的速度相对较慢。
package main
import (
"fmt"
"time"
)
func logger(ch chan string) {
for log := range ch {
fmt.Println("Logging:", log)
time.Sleep(time.Millisecond * 100) // 模拟较慢的日志处理
}
}
func main() {
ch := make(chan string, 10) // 有10个缓冲的chan
for i := 0; i < 20; i++ {
log := fmt.Sprintf("Log entry %d", i)
ch <- log
fmt.Println("Generated log:", log)
}
close(ch)
time.Sleep(time.Second * 2) // 等待日志处理完毕
}
在这种场景下,有缓冲的chan能暂存日志数据,避免生产者因消费者处理慢而阻塞,提升整体性能,适合生产和消费速度不匹配且允许一定数据堆积的场景。