常做并发工作的朋友对互斥锁应该不陌生,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造成的。