1、写一个计算薪资的小程序
  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "runtime"
  6. )
  7. func main(){
  8. var salary int = 0
  9. var wg sync.Waitgroup
  10. fmt.Println("start")
  11. for i:=0;i<10;i++{
  12. wg.Add(2)
  13. go func(){
  14. defer wg.Done()
  15. for i := 0;i<1000;i++{
  16. salary+=10
  17. //salary 0
  18. //内存->寄存器 ->0
  19. //寄存器-> 内存 ->10
  20. runtime.Gosched()
  21. }
  22. }()
  23. go func(){
  24. defer wg.Done()
  25. for i :=0;i<1000;i++{
  26. salary-=10
  27. //salary 0
  28. //内存->寄存器 ->0
  29. //寄存器-> 内存 ->-10
  30. runtime.Gosched()
  31. }
  32. }
  33. }
  34. }

按照正常的逻辑最后的结果应该是0;单由于数据存放在寄存器中的数据没有锁,加和减两个例程在执行的过程当中,都会从寄存器中拿或者写数据, A例程拿到的是0,同时B例程拿到的也是0,A例程执行完一次循环写入寄存器为10,B例程执行完一次循环写入寄存器为-10,这时寄存器内变量的值就是-10,所以以上程序最终结果是不固定的,没有达到我们的期望。

2、使用sync.Mutex 实现例程对数据的锁
  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "runtime"
  6. )
  7. func main(){
  8. var salary int =0
  9. //定义锁
  10. var locker sync.Mutex
  11. var wg sync.Waitgroup
  12. fmt.Println("start")
  13. for i:=0;i<10;i++{
  14. wg.Add(2)
  15. go func(){
  16. defer wg.Done()
  17. for i := 0;i<1000;i++{
  18. locker.Lock() //对例程内的数据进行加锁,使得各个例程之间只有1个例程可以操作数据
  19. salary+=10
  20. locker.Unlock() //释放锁,使得其他例程可以操作数据
  21. runtime.Gosched()
  22. }
  23. }()
  24. go func(){
  25. defer wg.Done()
  26. for i :=0;i<1000;i++{
  27. locker.Lock() //对例程内的数据进行加锁,使得各个例程之间只有1个例程可以操作数据
  28. salary-=10
  29. locker.Unlock() //释放锁,使得其他例程可以操作数据
  30. runtime.Gosched()
  31. }
  32. }
  33. }
  34. }

使用锁之后就可以得到我们的预期结果了
注意,在使用锁的时候,尽量在小范围内,不要在大范围内使用锁

3、使用原子操作实现以上功能

在 sync/atomic包下有多个系列的原子操作函数

  • Add系列
  • Load系列
  • Store系列
  • Swap系列
  • CompareAndSwap系列
  1. package main
  2. import (
  3. "fmt"
  4. "sync/atomic"
  5. "runtime"
  6. )
  7. func main(){
  8. var salary int32 =0
  9. var psalary *int32 = &salary //原子操作函数传入的均为指针类型数据
  10. var wg sync.Waitgroup
  11. fmt.Println("start")
  12. for i:=0;i<10;i++{
  13. wg.Add(2)
  14. go func(){
  15. defer wg.Done()
  16. for i := 0;i<1000;i++{
  17. // salary+=10
  18. aotomic.AddInt32(psalary,10)
  19. runtime.Gosched()
  20. }
  21. }()
  22. go func(){
  23. defer wg.Done()
  24. for i :=0;i<1000;i++{
  25. // salary-=10
  26. aotomic.AddInt32(psalary,-10)
  27. runtime.Gosched()
  28. }
  29. }
  30. }
  31. }

原子操作实现比较底层,对类型也比较有限制,建议使用Mutex锁

文档更新时间: 2021-08-21 14:27   作者:张尚