Golang 的 goroutine 的 竞争解决方法 原子操作atomic(乐观锁)和互斥锁mutex(悲观锁)

竞争状态

如果两个多以上的goroutine在没有互相同步的情况下,访问某个共享的资源,并试图同时读或者写,就处于相互竞争的状态。
解决这种问题的方法就是在同一时刻只有一个goroutine对此资源进行读写操作。

package main

import (
  "fmt"
  "runtime"
  "sync"
)

var(
  counter int //所有goroutine都要操作的变量
  wg sync.WaitGroup
)
func main() {
  wg.Add(2)
  go incCounter(1)
  go incCounter(2)

  wg.Wait()
  fmt.Println("Final Counter:",counter)
}
//想象这里是要秒杀一个商品,然后从数据库读取的某产品的销量,用户购买了1件 销量+1
func incCounter(c int)  {
  defer wg.Done() //程序结束时候,执行事件完成
  for count := 0; count < 2; count++ {
    /**
    counter++ // 如果只是操作不读取且使用++来解决,++是原子操作 不会出现错误
    runtime.Gosched() //当前goroutine从线程退出,并放回队列,等待逻辑处理器处理 模拟发生goroutine切换
     */
    //先读取值,在操作
    value := counter
    runtime.Gosched() //当前goroutine从线程退出,并放回队列,等待逻辑处理器处理 模拟发生goroutine切换
    value++ //发生业务变动,如计算销量或者金额等
    counter = value
  }
}

解决方案

  1. 原子函数
    原子函数能够很底层的通过锁机制来同步访问整型变量和指针。
func incCounter(c int)  {
  defer wg.Done() //程序结束时候,执行事件完成
  for count := 0; count < 2; count++ {
    atomic.AddInt32(&counter,1) //利用原子操作进行+1 等同于 counter++ 
    runtime.Gosched()
  }
}

另一种方式就是利用标志位作对比,类似乐观锁。

package main

import (
  "fmt"
  "sync"
  "sync/atomic"
  "time"
)

var(
  counter int32 //标志位
  wg sync.WaitGroup
)
func main() {
  wg.Add(2)
  go doWork("A")
  go doWork("B")

  time.Sleep(1*time.Second)

  fmt.Println("Shutdown Now!")
  atomic.StoreInt32(&counter,1) //设置标志位为1

  wg.Wait()
}
func doWork(name string) {
  defer wg.Done()

  for  {
    fmt.Printf("Doing %s Work\n",name)

    time.Sleep(100*time.Millisecond)

    if atomic.LoadInt32(&counter) == 1 { //这里比对标志位 结果一直则直接退出
      fmt.Printf("Shutting %s Down\n",name)
      break
    }
  }
}
  1. 互斥锁
    保证同一时间只有一个goroutine可以执行代码,类似悲观锁!
package main

import (
  "fmt"
  "runtime"
  "sync"
)

var(
  c int
  wg sync.WaitGroup
  mu sync.Mutex
)
func main() {
  wg.Add(2)
  go inc()
  go inc()
  wg.Wait()
  fmt.Printf("C value:%d\n",c)
}
func inc() {
  defer wg.Done()

  for count := 0; count < 2; count++ {
    mu.Lock() //上锁 只有单个goroutine操作
    v := c
    runtime.Gosched()
    v++
    c = v
    mu.Unlock() //解锁
  }
}
posted @ 2022-05-30 23:42  青柚  阅读(314)  评论(0编辑  收藏  举报