1、互斥锁

互斥锁是一种常用的控制共享的资源访问的方法,它能够保证同时只有一个 goroutine 可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func add() {
  7. defer wg.Done()
  8. //lock.Lock() // 加锁
  9. x++
  10. //lock.Unlock() // 解锁
  11. }
  12. var x = 0
  13. var wg sync.WaitGroup
  14. var lock sync.Mutex // 定义互斥锁
  15. func main() {
  16. wg.Add(100)
  17. for i := 0; i < 100; i++ {
  18. go add() // 启动100个并发
  19. }
  20. wg.Wait()
  21. fmt.Println(x)
  22. }

使用互斥锁能够保证同一时间有且只有一个 goroutine 进入临界区,其他的 goroutine 则在等候锁;当互斥锁释放后,等待的 goroutine 才可以获取锁进入临界区, 多个 goroutine 同时等待一个锁时,唤醒的策略是随机的。

2、读写锁

在读多写少的环境中,可以优先使用读写互斥锁(sync.RWMutex),它比互斥锁更加高效。sync包中RWMutex提供了读写互斥锁的封装。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. // 效率对比
  8. // 声明读写锁
  9. var rwlock sync.RWMutex
  10. var mutex sync.Mutex
  11. var wg sync.WaitGroup
  12. // 全局变量
  13. var x int
  14. // 写数据
  15. func write() {
  16. //mutex.Lock()
  17. rwlock.Lock()
  18. x += 1
  19. fmt.Println("x",x)
  20. time.Sleep(10 * time.Millisecond)
  21. //mutex.Unlock()
  22. rwlock.Unlock()
  23. wg.Done()
  24. }
  25. func read(i int) {
  26. //mutex.Lock()
  27. rwlock.RLock()
  28. time.Sleep(time.Millisecond)
  29. fmt.Println(x)
  30. //mutex.Unlock()
  31. rwlock.RUnlock()
  32. wg.Done()
  33. }
  34. // 互斥锁执行时间:18533117400
  35. // 读写锁执行时间:1312065700
  36. func main() {
  37. start := time.Now()
  38. wg.Add(1)
  39. go write()
  40. for i := 0; i < 1000; i++ {
  41. wg.Add(1)
  42. go read(i)
  43. }
  44. wg.Wait()
  45. fmt.Println("运行时间:", time.Now().Sub(start))
  46. }

3、map锁

Go语言中内置的map不是并发安全的。
并发读是安全的。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main() {
  7. wg := sync.WaitGroup{}
  8. m := make(map[int]int)
  9. // 添一些假数据
  10. for i := 0; i < 5; i++ {
  11. m[i] = i*i
  12. }
  13. // 遍历打印
  14. for i := 0; i < 5; i++ {
  15. wg.Add(1)
  16. go func(x int) {
  17. fmt.Println(m[x], "\t")
  18. wg.Done()
  19. }(i)
  20. }
  21. wg.Wait()
  22. fmt.Println(m)
  23. }

并发写则不安全

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func main() {
  7. wg := sync.WaitGroup{}
  8. //m := make(map[int]int)
  9. var m = sync.Map{}
  10. // 并发写
  11. for i := 0; i < 100; i++ {
  12. wg.Add(1)
  13. go func(i int) {
  14. defer wg.Done()
  15. m.Store(i,i*i)
  16. }(i)
  17. }
  18. wg.Wait()
  19. fmt.Println(m.Load(1))
  20. fmt.Println(m.Load(2))
  21. }