互斥锁(sync.Mutex)的使用

  多个协程并发执行可能会产生数据竞争问题,如下面的代码就有数据竞争问题:
 
package main

import (
   "fmt"
   "time"
)

func main() {
   num := 0

   for i := 0; i < 100000; i++ {
      go func(i int) {
         num++
         fmt.Printf("协程%d将num自增1,此时num为%d \n", i, num)

         time.Sleep(time.Millisecond) // 模拟耗时操作
      }(i)
   }

   time.Sleep(time.Second)
}

//========== 输出过程·开始 ==========//
// ……
// 协程10272将num自增1,此时num为10421
// 协程10084将num自增1,此时num为10421
// 协程10092将num自增1,此时num为10422
// 协程10085将num自增1,此时num为10422
// 协程10086将num自增1,此时num为10422
// 协程10273将num自增1,此时num为10422
// 协程10274将num自增1,此时num为10423
// 协程10275将num自增1,此时num为10424
// 协程10276将num自增1,此时num为10425
// 协程10087将num自增1,此时num为10422
// 协程10277将num自增1,此时num为10426
// 协程10278将num自增1,此时num为10427
// 协程10279将num自增1,此时num为10428
// 协程10088将num自增1,此时num为10426
// 协程10093将num自增1,此时num为10429
// 协程10280将num自增1,此时num为10429
// 协程10094将num自增1,此时num为10429
// 协程10095将num自增1,此时num为10430
// 协程10096将num自增1,此时num为10430
// ……
//========== 输出过程·结束 ==========//

//========== 总结 ==========//
// 1、想知道代码有没有数据竞争问题可以在执行“go run”时加入“-race”参数,即:go run -race main.go,
//    如果有数据竞争问题会出现“exit status 66”。
 
  在协程中加锁:
 
package main

import (
   "fmt"
   "sync"
   "time"
)

func main() {
   num := 0
   lock := sync.Mutex{}

   for i := 0; i < 100000; i++ {
      go func(i int) {
         lock.Lock() // 加锁
         num++
         fmt.Printf("协程%d将num自增1,此时num为%d \n", i, num)
         lock.Unlock() // 解锁

         time.Sleep(time.Millisecond) // 模拟耗时操作
      }(i)
   }

   time.Sleep(time.Second)
}
 
  重要提醒:并不建议使用加互斥锁的方式来处理协程间的数据共享问题,而应该使用管道(channel)。

Copyright © 2024 码农人生. All Rights Reserved