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
}
}
解决方案
- 原子函数
原子函数能够很底层的通过锁机制来同步访问整型变量和指针。
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
}
}
}
- 互斥锁
保证同一时间只有一个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() //解锁
}
}
标签:wg,count,counter,++,goroutine,sync,Golang,互斥 来源: https://www.cnblogs.com/ikai/p/16328837.html