b.Withdraw(30, "马伊琍")
fmt.Println("-----------------")
fmt.Println("剩余余额", b.Balance())
}
运行上面的代码会输出:
初始化余额 80
[-] 30 马伊琍
-----------------
剩余余额 50
没错!
不错在现实生活中,一个银行账号可以有很多个附属卡,不同的附属卡都可以对同一个账号进行存取钱,所以我们来修改一下代码:
func main() {
balance := 80
b := NewBank(bank.NewSimpleAccount(balance))
fmt.Println("初始化余额", b.Balance())
done := make(chan bool)
go func() { b.Withdraw(30, "马伊琍"); done <- true }()
go func() { b.Withdraw(10, "姚笛"); done <- true }()
//等待 goroutine 执行完成
<-done
<-done
fmt.Println("-----------------")
fmt.Println("剩余余额", b.Balance())
}
这儿两个附属卡并发的从账号里取钱,来看看输出结果:
初始化余额 80
[-] 30 马伊琍
[-] 10 姚笛
-----------------
剩余余额 70
这下把文章高兴坏了:)
结果当然是错误的,剩余余额应该是40而不是70,那么让我们看看到底哪儿出问题了。
问题
当并发访问共享资源时,无效状态有很大可能会发生。
在我们的例子中,当两个附属卡同一时刻从同一个账号取钱后,我们最后得到银行账号(即共享资源)错误的剩余余额(即无效状态)。
我们来看一下执行时候的情况:
处理情况
--------------
_马伊琍_|_姚笛_
1. 获取余额 80 | 80
2. 取钱 -30 | -10
3. 当前剩余 50 | 70
... | ...
4. 设置余额 50 ? 70 //该先设置哪个好呢?
5. 后设置的生效了
--------------
6. 剩余余额 70
上面 ... 的地方描述了我们 add_some_latency 实现的延时状况,现实世界经常发生延迟情况。所以最后的剩余余额就由最后设置余额的那个附属卡决定。
解决办法
我们通过两种方法来解决这个问题:
1.共享内存的解决方案










