面试题答案
一键面试可能出现的问题
- 死锁:如果发送方Goroutine在没有接收方准备好接收数据时,持续向Channel发送数据,而Channel是无缓冲的,就会导致发送方Goroutine阻塞。同理,如果接收方Goroutine在没有数据发送进来时尝试接收,也会阻塞。如果所有Goroutine都处于阻塞状态,就会发生死锁。
- 数据竞争:当多个Goroutine同时读写共享资源(这里的Channel本质上是共享资源)时,如果没有适当的同步机制,可能会导致数据竞争问题,虽然Go语言的Channel在设计上避免了大部分数据竞争问题,但如果使用不当(如在Channel关闭后继续发送数据等),仍可能出现问题。
使用Select语句优化并发控制
select
语句用于在多个通信操作(如发送或接收数据到Channel)之间进行选择。它会阻塞,直到其中一个通信操作可以继续执行。
示例代码
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
// 发送方Goroutine
for i := 0; i < 3; i++ {
go func(id int) {
for j := 0; j < 3; j++ {
ch <- id*10 + j
}
close(ch) // 发送完成后关闭Channel
}(i)
}
// 接收方Goroutine
go func() {
for {
select {
case data, ok := <-ch:
if!ok {
return
}
fmt.Println("Received:", data)
}
}
}()
// 防止主线程退出
select {}
}
在上述代码中:
- 多个发送方Goroutine向
ch
Channel发送数据,每个发送方发送3个数据,发送完成后关闭Channel。 - 接收方Goroutine使用
select
语句从Channel接收数据。select
语句中的case
语句等待ch
Channel有数据可读。当ch
关闭且没有数据可读时,ok
为false
,接收方Goroutine退出。 - 主线程通过
select {}
阻塞,防止程序过早退出。