面试题答案
一键面试适用场景
在需要非阻塞操作的场景下,default
分支是最合适的。例如在从通道读取数据时,如果通道为空,不想让程序阻塞等待数据到来,而是希望立即执行其他逻辑,就可以使用 default
分支。比如在一个处理多个任务的系统中,某个任务需要从通道获取数据,但该任务不能因为通道暂时无数据而阻塞,此时可以用 default
分支继续执行其他任务。
竞争条件和资源浪费陷阱及避免方法
- 竞争条件:
- 陷阱:多个
goroutine
同时访问并尝试从通道读取或写入数据,可能导致数据不一致。例如,在一个select
语句中有多个case
分支和default
分支,多个goroutine
同时执行这个select
语句,可能会出现某个goroutine
先执行了default
分支,而此时通道其实有数据,导致数据被其他goroutine
读取,出现竞争。 - 避免方法:对通道的读写操作进行合理的同步控制。可以使用互斥锁(
sync.Mutex
)来保护对通道的操作,确保同一时间只有一个goroutine
可以对通道进行操作。
- 陷阱:多个
- 资源浪费:
- 陷阱:如果
default
分支执行频率过高,会导致 CPU 资源浪费。例如在一个循环中频繁执行包含default
分支的select
语句,而通道很少有数据到来,就会造成default
分支不断执行,浪费 CPU 资源。 - 避免方法:减少不必要的
default
分支执行。可以使用time.Sleep
来适当降低执行频率,或者在循环中添加一些条件判断,只有在满足一定条件时才执行select
语句。
- 陷阱:如果
代码示例
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch <- 42
}()
for {
select {
case data := <-ch:
fmt.Printf("Received data: %d\n", data)
return
default:
fmt.Println("Channel is empty, doing other work...")
time.Sleep(1 * time.Second)
}
}
}
在上述代码中,default
分支在通道为空时执行,打印提示信息并休眠 1 秒,避免了高频执行 default
分支造成资源浪费。同时通过 go
协程模拟数据在 2 秒后写入通道,当通道有数据时,case
分支被执行,获取并打印数据后程序结束。