gopl-zh.github.com/ch9/ch9-03.md
2018-06-09 16:36:07 +00:00

2.2 KiB
Raw Blame History

9.3. sync.RWMutex读写锁

在100刀的存款消失时不做记录多少还是会让我们有一些恐慌Bob写了一个程序每秒运行几百次来检查他的银行余额。他会在家在工作中甚至会在他的手机上来运行这个程序。银行注意到这些陡增的流量使得存款和取款有了延时因为所有的余额查询请求是顺序执行的这样会互斥地获得锁并且会暂时阻止其它的goroutine运行。

由于Balance函数只需要读取变量的状态所以我们同时让多个Balance调用并发运行事实上是安全的只要在运行的时候没有存款或者取款操作就行。在这种场景下我们需要一种特殊类型的锁其允许多个只读操作并行执行但写操作会完全互斥。这种锁叫作“多读单写”锁multiple readers, single writer lockGo语言提供的这样的锁是sync.RWMutex

var mu sync.RWMutex
var balance int
func Balance() int {
	mu.RLock() // readers lock
	defer mu.RUnlock()
	return balance
}

Balance函数现在调用了RLock和RUnlock方法来获取和释放一个读取或者共享锁。Deposit函数没有变化会调用mu.Lock和mu.Unlock方法来获取和释放一个写或互斥锁。

在这次修改后Bob的余额查询请求就可以彼此并行地执行并且会很快地完成了。锁在更多的时间范围可用并且存款请求也能够及时地被响应了。

RLock只能在临界区共享变量没有任何写入操作时可用。一般来说我们不应该假设逻辑上的只读函数/方法也不会去更新某一些变量。比如一个方法功能是访问一个变量,但它也有可能会同时去给一个内部的计数器+1译注可能是记录这个方法的访问次数啥的或者去更新缓存——使即时的调用能够更快。如果有疑惑的话请使用互斥锁。

RWMutex只有当获得锁的大部分goroutine都是读操作而锁在竞争条件下也就是说goroutine们必须等待才能获取到锁的时候RWMutex才是最能带来好处的。RWMutex需要更复杂的内部记录所以会让它比一般的无竞争锁的mutex慢一些。