在golang中,实现了两种锁:

  • Mutex,互斥锁
  • RWMutex:读写锁,RWMutex基于Mutex实现

Mutex(互斥锁)

  1. Mutex为互斥锁,Lock()加锁,Unlock()解锁
  2. 在一个goroutine获得Mutex之后,其他goroutine只能等到这个goroutine释放该Mutex
  3. 使用Lock()加锁之后,不能继续对其加锁,知道利用Unlock()解锁之后才能解锁
  4. 在Lock()之前使用Unlock()会导致panic异常
  5. 已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他groutine对其解锁
  6. 适用于读写不确定,并且只有一个读或者写的场景

加锁和解锁示例

  1. package main
  2. import (
  3. "time"
  4. "fmt"
  5. "sync"
  6. )
  7. func main() {
  8. var mutex sync.Mutex
  9. fmt.Println("Lock the lock")
  10. mutex.Lock()
  11. fmt.Println("The lock is locked")
  12. channels := make([]chan int, 4)
  13. for i := 0; i < 4; i++ {
  14. channels[i] = make(chan int)
  15. go func(i int, c chan int) {
  16. fmt.Println("Not lock: ", i)
  17. mutex.Lock()
  18. fmt.Println("Locked: ", i)
  19. time.Sleep(time.Second)
  20. fmt.Println("Unlock the lock: ", i)
  21. mutex.Unlock()
  22. c <- i
  23. }(i, channels[i])
  24. }
  25. time.Sleep(time.Second)
  26. fmt.Println("Unlock the main goroutine lock")
  27. mutex.Unlock()
  28. time.Sleep(time.Second)
  29. for _, c := range channels {
  30. <-c
  31. }
  32. }

  1. // 程序输出
  2. Lock the lock
  3. The lock is locked
  4. Not lock: 1
  5. Not lock: 0
  6. Not lock: 2
  7. Not lock: 3
  8. Unlock the main goroutine lock
  9. Locked: 1
  10. Unlock the lock: 1
  11. Locked: 0
  12. Unlock the lock: 0
  13. Locked: 2
  14. Unlock the lock: 2
  15. Locked: 3
  16. Unlock the lock: 3

在解锁之前加锁会导致死锁

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main(){
  7. var mutex sync.Mutex
  8. mutex.Lock()
  9. fmt.Println("Locked")
  10. mutex.Lock()
  11. }
  1. //程序输出
  2. Locked
  3. fatal error: all goroutines are asleep - deadlock!

RWMutex(读写锁)

  1. RWMutex是单写多读锁,该锁可以加多个读锁或者一个写锁
  2. 读锁占用的情况下会阻止写,不会阻止读,多个goroutine可以同时获取读锁
  3. 写锁会阻止其他goroutine(无论读和写进来)进来,整个锁由该goroutine 独占
  4. 适用于读多写少的场景

写锁:Lock() 和 Unlock()

  1. Lock()加写锁,Unlock()解写锁
  2. 如果在加写锁之前已经由其他的读锁或者写锁,则Lock()会阻塞直到该锁可用。已经阻塞的Lock()调用会从获得的锁中排除新的读取器,即写锁的权限高于读锁,有写锁时优先进行写锁定
  3. 在Lock()使用Unlock()会导致panic异常

    读锁:RLock() 和 RUnlock()

  4. Rlock()加读锁,RUnlock()解读锁

  5. RLock()加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加载多个
  6. RUnlock()解读锁,Runlock()撤销单次RLock()调用,对于其他同时存在的读锁则没有效果
  7. 在没有读锁的情况下调用Runlock()会导致panic错误

下面展示一下我们的写锁:

  1. // 在这里,我们使用一个
  2. type Handler struct {
  3. mx sync.RWMutex
  4. lastActive time.Time
  5. }
  6. func (h *Handler) Run() {
  7. h.mx.Lock()
  8. {
  9. h.lastActive = time.Now()
  10. }
  11. h.mx.Unlock()
  12. }

总结一下:互斥锁定就是单读单写,只能有一种状态;而读写锁则是可以用读锁和写锁,写锁的优先级别高于读锁。

参考: golang 中 sync.Mutex 和 sync.RWMutex