面试题答案
一键面试简单Go并发程序场景及竞态条件
- 场景描述:假设有一个银行账户,有多个并发的存钱操作。账户余额是一个共享变量,多个存钱操作同时对其进行修改。
package main
import (
"fmt"
)
var balance int
func deposit(amount int) {
balance = balance + amount
}
func main() {
for i := 0; i < 10; i++ {
go deposit(10)
}
fmt.Println("Final balance:", balance)
}
- 竞态条件产生:在上述代码中,
balance = balance + amount
这一操作不是原子性的。在多个goroutine同时执行deposit
函数时,可能出现以下情况:- 一个goroutine读取了
balance
的值,假设为100。 - 另一个goroutine也读取了
balance
的值,同样是100。 - 第一个goroutine将读取的值加上10,得到110,然后将110写回
balance
。 - 第二个goroutine也将读取的值加上10,得到110,然后将110写回
balance
。但实际上应该是120,因为有两次存钱操作。这就是竞态条件,多个goroutine同时读写共享变量balance
导致结果不正确。
- 一个goroutine读取了
使用Go race detector发现竞态条件
- 运行方式:在命令行中使用
go run -race
命令来运行程序。- 例如,假设上述代码保存为
main.go
,在终端进入该文件所在目录,执行go run -race main.go
。
- 例如,假设上述代码保存为
- 输出示例:如果存在竞态条件,race detector会输出详细的信息,指出哪个goroutine在什么时间对哪个共享变量进行了读写操作,以及相关的堆栈信息,帮助开发者定位问题。例如:
==================
WARNING: DATA RACE
Write at 0x00c0000100d8 by goroutine 7:
main.deposit()
/path/to/main.go:7 +0x33
main.main.func1()
/path/to/main.go:13 +0x2e
Previous read at 0x00c0000100d8 by goroutine 6:
main.deposit()
/path/to/main.go:7 +0x23
main.main.func1()
/path/to/main.go:13 +0x2e
Goroutine 7 (running) created at:
main.main()
/path/to/main.go:13 +0x78
Goroutine 6 (finished) created at:
main.main()
/path/to/main.go:13 +0x78
==================
Final balance: 80
Found 1 data race(s)
exit status 66
这样就能发现并定位到代码中的竞态条件。