01
介绍
Golang 语言天生支持并发,关于并发编程,Golang 语言还有一句口号:“不要通过共享内存进行通信;而是通过通信共享内存”。
但是,通过“共享内存进行通信”的方式作为并发编程的解决方案在传统的编程语言中更为流行。在 Golang 语言标准库 sync 包中也提供了“通过共享内存进行通信”的并发编程解决方案。
其中,在 sync 包中最重要的同步工具就是 sync.Mutex
和 sync.RWMutex
。因为在之前的文章已经介绍过二者的使用,所以本文我们不再赘述。本文主要介绍使用二者的注意事项和二者的区别。
02
Mutex
Mutex 也称为互斥锁,互斥锁就是互相排斥的锁,它可以用作保护临界区的共享资源,保证同一时刻只有一个 goroutine 操作临界区中的共享资源。互斥锁 Mutex
类型有两个方法,Lock
和 Unlock
。
使用互斥锁的注意事项:
Unlock
方法,将会引发运行时错误。Lock
方法和 Unlock
方法要成对使用,不要忘记将锁定的互斥锁解锁,一般做法是使用 defer。互斥锁源码:
type Mutex struct {
state int32 // 互斥锁的状态
sema uint32 // 信号量,用于控制互斥锁的状态
}
03
RWMutex
RWMutex 也称为读写互斥锁,读写互斥锁就是读取/写入互相排斥的锁。它可以由任意数量的读取操作的 goroutine 或单个写入操作的 goroutine 持有。读写互斥锁 RWMutex
类型有五个方法,Lock
,Unlock
,Rlock
,RUnlock
和 RLocker
。其中,RLocker 返回一个 Locker 接口,该接口通过调用 rw.RLock
和 rw.RUnlock
来实现 Lock 和 Unlock 方法。
使用读写互斥锁的注意事项:
Lock
去锁定临界区的共享资源,如果临界区的共享资源已被(读锁或写锁)锁定,这个写锁操作的 goroutine 将被阻塞直到解锁。读写互斥锁源码:
type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}
04
Mutex 和 RWMutex 的区别
RWMutex 和 Mutex 的区别是 RWMutex 将对临界区的共享资源的读写操作做了区分,RWMutex 可以针对读写操作做不同级别的锁保护。
RWMutex 读写锁中包含读锁和写锁,它的 Lock
和 Unlock
方法用作写锁保护,它的 RLock
和 RUnlock
方法用作读锁保护。
RWMutex 读写锁中的读锁和写锁关系如下:
但是,在读锁处于锁定状态时,操作锁定读锁的 goroutine 不会被阻塞。我们可以理解为读锁保护的临界区的共享资源,多个读操作可以同时执行。
05
总结
本文我们介绍了 Golang 语言中的基本同步原语互斥锁和读写互斥锁使用时的注意事项,然后总结了二者的区别。读写互斥锁可以对临界区的共享资源做更加细粒度的访问控制,在读锁持有锁时,其他操作读锁的 goroutine 不被被阻塞,(也就是说不限制对临界区的共享资源的并发读)所以在读多写少的场景,我们可以使用读写互斥锁替代互斥锁,提升应用程序的性能。