1.9 并发保护

1.9.1【必须】禁止在闭包中直接调用循环变量

  • 在循环中启动协程,当协程中使用到了循环的索引值,由于多个协程同时使用同一个变量会产生数据竞争,造成执行结果异常。
  1. // bad
  2. func main() {
  3. runtime.GOMAXPROCS(runtime.NumCPU())
  4. var group sync.WaitGroup
  5. for i := 0; i < 5; i++ {
  6. group.Add(1)
  7. go func() {
  8. defer group.Done()
  9. fmt.Printf("%-2d", i) //这里打印的i不是所期望的
  10. }()
  11. }
  12. group.Wait()
  13. }
  14. // good
  15. func main() {
  16. runtime.GOMAXPROCS(runtime.NumCPU())
  17. var group sync.WaitGroup
  18. for i := 0; i < 5; i++ {
  19. group.Add(1)
  20. go func(j int) {
  21. defer func() {
  22. if r := recover(); r != nil {
  23. fmt.Println("Recovered in start()")
  24. }
  25. group.Done()
  26. }()
  27. fmt.Printf("%-2d", j) // 闭包内部使用局部变量
  28. }(i) // 把循环变量显式地传给协程
  29. }
  30. group.Wait()
  31. }

1.9.2【必须】禁止并发写map

  • 并发写map容易造成程序崩溃并异常退出,建议加锁保护
    1. // bad
    2. func main() {
    3. m := make(map[int]int)
    4. //并发读写
    5. go func() {
    6. for {
    7. _ = m[1]
    8. }
    9. }()
    10. go func() {
    11. for {
    12. m[2] = 1
    13. }
    14. }()
    15. select {}
    16. }

    1.9.3【必须】确保并发安全

敏感操作如果未作并发安全限制,可导致数据读写异常,造成业务逻辑限制被绕过。可通过同步锁或者原子操作进行防护。

通过同步锁共享内存

  1. // good
  2. var count int
  3. func Count(lock *sync.Mutex) {
  4. lock.Lock()// 加写锁
  5. count++
  6. fmt.Println(count)
  7. lock.Unlock()// 解写锁,任何一个Lock()或RLock()均需要保证对应有Unlock()或RUnlock()
  8. }
  9. func main() {
  10. lock := &sync.Mutex{}
  11. for i := 0; i < 10; i++ {
  12. go Count(lock) //传递指针是为了防止函数内的锁和调用锁不一致
  13. }
  14. for {
  15. lock.Lock()
  16. c := count
  17. lock.Unlock()
  18. runtime.Gosched()//交出时间片给协程
  19. if c > 10 {
  20. break
  21. }
  22. }
  23. }
  • 使用sync/atomic执行原子操作
  1. // good
  2. import (
  3. "sync"
  4. "sync/atomic"
  5. )
  6. func main() {
  7. type Map map[string]string
  8. var m atomic.Value
  9. m.Store(make(Map))
  10. var mu sync.Mutex // used only by writers
  11. read := func(key string) (val string) {
  12. m1 := m.Load().(Map)
  13. return m1[key]
  14. }
  15. insert := func(key, val string) {
  16. mu.Lock() // 与潜在写入同步
  17. defer mu.Unlock()
  18. m1 := m.Load().(Map) // 导入struct当前数据
  19. m2 := make(Map) // 创建新值
  20. for k, v := range m1 {
  21. m2[k] = v
  22. }
  23. m2[key] = val
  24. m.Store(m2) // 用新的替代当前对象
  25. }
  26. _, _ = read, insert
  27. }