Go 并发原语
并发问题出现的原因---> 在Go开发中如何监测到这个问题---> Go 是如何解决的
https://songlh.github.io/paper/go-study.pdf
问题描述:
package main import( "fmt" "sync" ) func main(){ var count = 0 // 使用 WaitGroup等待 10个goroutine 完成 var wg sync.WaitGroup wg.Add(10) for i :=0;i< 10; i++{ go func () { defer wg.Done() // 对变量count 执行10次加1 for j:=0;j<100000; j++{ count++ } }() } // 等10个goroutine 完成 wg.Wait() fmt.Println(count) }
出错原因
分析代码---> 这段代码一共启动了10个goroutine, 这10个goroutine 可能同时进行count++的操作
如何监测
Data Race Detector:https://go.dev/doc/articles/race_detector
groutine8 对内存地址0x00c00012c078 有度的操作 (go文件的第17行), 同时groutine7 对内存地址有写操作(go文件的第17行)
Data Race Deltector 只能在代码触发并发问题后才给予反馈
go tool compile -race -S .\testMutext.go
可以查看 go 编译后的代码,之前了解到, 产生并发问题是在 count++ 附近,重点关注一下
代码中增加了runtine.recefuncenter, runtime.recered, runtime.recewrite, runtime.recefuncexit等监测data race问题
如何解决
Go标准库中, package sync提供了锁相关的一系列同步原语,Mutex 实现了该包提供的一个 Locker的接口
type Locker interface { Lock() Unlock() }
互斥锁Mutex 提供了两个方法 Lock和Unlock,进入临界区前调用Lock方法, 退出临界区的时候调用Unlock方法
func(m *Mutex)Lock() func(m*Mutex)Unlock()
Mutex 嵌入到Struct中使用
type Counter struct{ mu sync.Mutex Count unint64 }
使用Mutex解决并发锁问题的几种代码形式
1.
package main import ( "fmt" "sync" ) func main(){ // 互斥锁保护计数器 var mu sync.Mutex // 计数器的值 var count = 0 // 辅助变量,用来确认所有的goroutine 都完成 var wg sync.WaitGroup wg.Add(10) // 启动 10个gourontine for i:=0; i<10; i++{ go func () { defer wg.Done() // 累加 10万次 for j:=0; j< 100000;j++{ mu.Lock() count++ mu.Unlock() } }() } wg.Wait() fmt.Println(count) }
package main import ( "fmt" "sync" ) func main() { var counter Counter var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go func() { defer wg.Done() for j := 0; j < 100000; j++ { counter.Lock() counter.Count++ counter.Unlock() } }() } wg.Wait() fmt.Println(counter.Count) } type Counter struct{ sync.Mutex Count uint64 }
package main import( "fmt" "sync" ) func main(){ //封装好的计数器 var counter Counter var wg sync.WaitGroup wg.Add(10) //启动 10个goroutine for i:=0;i<10;i++{ go func(){ defer wg.Done() // 执行10万次 累加 for j:=0; j<100000;j++{ counter.Incr() //受到锁保护的方法 } }() } wg.Wait() fmt.Println(counter.Count()) } // 线程安全的计数器类型 type Counter struct{ CounterType int Name string mu sync.Mutex count uint64 } // 加1 的方法,内部使用互斥锁保护 func (c *Counter) Incr(){ c.mu.Lock() c.count++ c.mu.Unlock() } // 得到计数器的值,也需要锁保护 func (c *Counter) Count() uint64{ c.mu.Lock() defer c.mu.Unlock() return c.count }