常做并发工作的朋友对互斥锁应该不陌生,Golang里互斥锁需要确保的是某段时间内,不能有多个协程同时访问一段代码(临界区)。
MutexLock()Unlock()
type Mutex
func (m *Mutex) Lock(){}
func (m *Mutex) Unlock(){}
MutexMutex
经过了上面这么“官方”的介绍,举个例子:你在工商银行有100元存款,这张卡绑定了支付宝和微信,在中午12点你用支付宝支付外卖30元,你在微信发红包,抢到10块。银行需要按顺序执行上面两件事,先减30再加10或者先加10再减30,结果都是80,但如果同时执行,结果可能是,只减了30或者只加了10,即你有70元或者你有110元。前一个结果是你赔了,后一个结果是银行赔了,银行可不希望把这种事算错。
看看实际使用吧:创建一个银行,银行里存每个账户的钱,存储查询都加了锁操作,这样银行就不会算错账了。
银行的定义:
type Bank struct {
sync.Mutex
saving map[string]int // 每账户的存款金额
}
func NewBank() *Bank {
b := &Bank{
saving: make(map[string]int),
}
return b
}
银行的存取钱:
// Deposit 存款
func (b *Bank) Deposit(name string, amount int) {
b.Lock()
defer b.Unlock()
if _, ok := b.saving[name]; !ok {
b.saving[name] = 0
}
b.saving[name] += amount
}
// Withdraw 取款,返回实际取到的金额
func (b *Bank) Withdraw(name string, amount int) int {
b.Lock()
defer b.Unlock()
if _, ok := b.saving[name]; !ok {
return 0
}
if b.saving[name] < amount {
amount = b.saving[name]
}
b.saving[name] -= amount
return amount
}
// Query 查询余额
func (b *Bank) Query(name string) int {
b.Lock()
defer b.Unlock()
if _, ok := b.saving[name]; !ok {
return 0
}
return b.saving[name]
}
模拟操作:小米支付宝存了100,并且同时花了20。
func main() {
b := NewBank()
go b.Deposit("xiaoming", 100)
go b.Withdraw("xiaoming", 20)
go b.Deposit("xiaogang", 2000)
time.Sleep(time.Second)
fmt.Printf("xiaoming has: %dn", b.Query("xiaoming"))
fmt.Printf("xiaogang has: %dn", b.Query("xiaogang"))
}
结果:先存后花。
➜ sync_pkg git:(master) ✗ go run mutex.go
xiaoming has: 80
xiaogang has: 2000
也可能是:先花后存,因为先花20,因为小明没钱,所以没花出去。
➜ sync_pkg git:(master) ✗ go run mutex.go
xiaoming has: 100
xiaogang has: 2000
这个例子只是介绍了mutex的基本使用,如果你想多研究下mutex,那就去我的Github(阅读原文)下载下来代码,自己修改测试。Github中还提供了没有锁的例子,运行多次总能碰到错误:
fatal error: concurrent map writes
这是由于并发访问map造成的。