面试题答案
一键面试数据竞争产生原因
- 多个goroutine并发访问:当多个goroutine同时尝试对共享资源(这里指通过chan进行数据交互相关的共享状态,比如chan的缓冲区状态等)进行读写操作时,如果没有适当的同步机制,就可能出现数据竞争。
- 非阻塞操作特性:在非阻塞操作中,由于操作不会等待,多个goroutine可能会在同一时间点尝试读写chan相关状态,例如在使用
select
语句进行非阻塞的send
或receive
操作时,不同goroutine的select
分支可能会同时触发,导致对chan的并发访问冲突。
避免数据竞争的代码示例
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
ch := make(chan int)
// 生产者
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 5; i++ {
select {
case ch <- i:
default:
fmt.Println("Producer: Channel is full, skipping", i)
}
}
close(ch)
}()
// 消费者
wg.Add(1)
go func() {
defer wg.Done()
for val := range ch {
fmt.Println("Consumer:", val)
}
}()
wg.Wait()
}
在上述代码中:
- 同步机制:使用
sync.WaitGroup
来确保所有goroutine完成任务后程序才退出,避免主程序提前退出导致未完成的goroutine产生数据竞争问题。 - 合理的chan使用:生产者通过
select
语句的default
分支进行非阻塞发送,当chan
满时跳过发送操作,避免了因盲目发送导致的与消费者读取操作的冲突。消费者使用for... range
循环来从chan
读取数据,直到chan
关闭,这种方式保证了数据读取的有序性和一致性,从而避免了数据竞争。