1、写一个计算薪资的小程序
package main
import (
"fmt"
"sync"
"runtime"
)
func main(){
var salary int = 0
var wg sync.Waitgroup
fmt.Println("start")
for i:=0;i<10;i++{
wg.Add(2)
go func(){
defer wg.Done()
for i := 0;i<1000;i++{
salary+=10
//salary 0
//内存->寄存器 ->0
//寄存器-> 内存 ->10
runtime.Gosched()
}
}()
go func(){
defer wg.Done()
for i :=0;i<1000;i++{
salary-=10
//salary 0
//内存->寄存器 ->0
//寄存器-> 内存 ->-10
runtime.Gosched()
}
}
}
}
按照正常的逻辑最后的结果应该是0;单由于数据存放在寄存器中的数据没有锁,加和减两个例程在执行的过程当中,都会从寄存器中拿或者写数据, A例程拿到的是0,同时B例程拿到的也是0,A例程执行完一次循环写入寄存器为10,B例程执行完一次循环写入寄存器为-10,这时寄存器内变量的值就是-10,所以以上程序最终结果是不固定的,没有达到我们的期望。
2、使用sync.Mutex 实现例程对数据的锁
package main
import (
"fmt"
"sync"
"runtime"
)
func main(){
var salary int =0
//定义锁
var locker sync.Mutex
var wg sync.Waitgroup
fmt.Println("start")
for i:=0;i<10;i++{
wg.Add(2)
go func(){
defer wg.Done()
for i := 0;i<1000;i++{
locker.Lock() //对例程内的数据进行加锁,使得各个例程之间只有1个例程可以操作数据
salary+=10
locker.Unlock() //释放锁,使得其他例程可以操作数据
runtime.Gosched()
}
}()
go func(){
defer wg.Done()
for i :=0;i<1000;i++{
locker.Lock() //对例程内的数据进行加锁,使得各个例程之间只有1个例程可以操作数据
salary-=10
locker.Unlock() //释放锁,使得其他例程可以操作数据
runtime.Gosched()
}
}
}
}
使用锁之后就可以得到我们的预期结果了
注意,在使用锁的时候,尽量在小范围内,不要在大范围内使用锁
3、使用原子操作实现以上功能
在 sync/atomic包下有多个系列的原子操作函数
- Add系列
- Load系列
- Store系列
- Swap系列
- CompareAndSwap系列
package main
import (
"fmt"
"sync/atomic"
"runtime"
)
func main(){
var salary int32 =0
var psalary *int32 = &salary //原子操作函数传入的均为指针类型数据
var wg sync.Waitgroup
fmt.Println("start")
for i:=0;i<10;i++{
wg.Add(2)
go func(){
defer wg.Done()
for i := 0;i<1000;i++{
// salary+=10
aotomic.AddInt32(psalary,10)
runtime.Gosched()
}
}()
go func(){
defer wg.Done()
for i :=0;i<1000;i++{
// salary-=10
aotomic.AddInt32(psalary,-10)
runtime.Gosched()
}
}
}
}
原子操作实现比较底层,对类型也比较有限制,建议使用Mutex锁
文档更新时间: 2021-08-21 14:27 作者:张尚