Golang 原子操作与互斥锁

先来看一个代码

package mainimport ("fmt""runtime""sync"
)var (counter  int32wg sync.WaitGroup
)func main() {wg.Add(2) // 设置需要等待的 goroutine 的数量为 2go addCounter("Gerald")go addCounter("Seligman")wg.Wait()fmt.Println("Final Counter is: %+v", counter)
}func addCounter(whoAmI string) {defer wg.Done()for count := 0; count < 2;  count++ {value := counter// Gosched使当前go程放弃处理器,以让其它go程运行。它不会挂起当前go程,因此当前go程未来会恢复执行。runtime.Gosched()value++counter = value}
}

Final Counter is: 2

首先这个程序是起了两个 goroutine 。每个 goroutine 都对 counter 进行了两次累加操作,所以理论上 counter 最后应该是 4 而不是 2 。

原因是因为,Gerald 协程获取到 counter 的值后,又让 Seligman 协程去获取 counter 的值。注意此时 counter 的值还没改变,所以他们两个协程都拿到 0 这个值。然后两个协程又将 value++ 后赋值给 counter,等于说做了两次 counter = 1 的操作。第二次循环也是一样的原理。所以最后的值是 2 。

这种情况可以用两中方式避免

  • Atomic 原子操作
  • Mutex 互斥锁

Atomic

for count := 0; count < 2;  count++ {value := counter// Gosched使当前go程放弃处理器,以让其它go程运行。它不会挂起当前go程,因此当前go程未来会恢复执行。runtime.Gosched()value++counter = value}

这段代码其实有点奇怪,为什么给 counter + 1 要写得这么多步。这其实是在模拟 CPU 内部再给你个值+1的时候需要做的操作。首先寄存器会把 counter 的值拿出来保存在寄存器里,然后在寄存器里进行+1操作,最后再把寄存器里的值放到CPU里(原理是这样,若位置错误,请及时提醒,见谅)。

而 Atomic 就是相当于在 CPU 里加锁,让这三步操作在执行的时候,当前协程不可以被调度切换,等这三步完成之后才可以被调度切换。

package mainimport ("fmt""runtime""sync""sync/atomic"
)var (counter  int32wg sync.WaitGroup
)func main() {wg.Add(2) // 设置需要等待的 goroutine 的数量为 2go addCounter("Gerald")go addCounter("Seligman")wg.Wait()fmt.Println("Final Counter is: ", counter)
}func addCounter(whoAmI string) {defer wg.Done()for count := 0; count < 2;  count++ {//value := counter// Gosched使当前go程放弃处理器,以让其它go程运行。它不会挂起当前go程,因此当前go程未来会恢复执行。//runtime.Gosched()////value++////counter = valueatomic.AddInt32(&counter, 1)runtime.Gosched()}
}

Final Counter is: 4

这次 counter 就变成 4 了,符合预期。

Mutex

这个应该很熟悉了,互斥锁,就是在对元素操作之前进行加锁,操作完了解锁。就可以保证数据一致了。

package mainimport ("fmt""sync"
)var (counter  int32wg sync.WaitGroupmutex sync.Mutex
)func main() {wg.Add(2) // 设置需要等待的 goroutine 的数量为 2go addCounter("Gerald")go addCounter("Seligman")wg.Wait()fmt.Println("Final Counter is: ", counter)
}func addCounter(whoAmI string) {defer wg.Done()for count := 0; count < 2;  count++ {//value := counter// Gosched使当前go程放弃处理器,以让其它go程运行。它不会挂起当前go程,因此当前go程未来会恢复执行。//runtime.Gosched()////value++////counter = value//atomic.AddInt32(&counter, 1)//runtime.Gosched()mutex.Lock()counter++mutex.Unlock()}
}

Final Counter is: 4

叮~

Golang 原子操作与互斥锁相关推荐

  1. Go语言的原子操作和互斥锁的区别

    这个系列的文章里介绍了很多并发编程里经常用到的技术,除了Context.计时器.互斥锁还有通道外还有一种技术--原子操作在一些同步算法中会被用到.今天的文章里我们会简单了解一下Go语言里对原子操作的支 ...

  2. 原子操作和互斥锁的区别

    这个系列的文章里介绍了很多并发编程里经常用到的技术,除了Context.计时器.互斥锁还有通道外还有一种技术--原子操作在一些同步算法中会被用到.今天的文章里我们会简单了解一下Go语言里对原子操作的支 ...

  3. 原子操作、互斥锁、读写锁

    原子操作 package mainimport ("fmt""sync""sync/atomic" //原子操作,比读写锁和互斥锁都要快,原 ...

  4. golang RWMutex读写互斥锁源码分析

    针对Golang 1.9的sync.RWMutex进行分析,与Golang 1.10基本一样除了将panic改为了throw之外其他的都一样. RWMutex是读写互斥锁.锁可以由任意数量的读取器或单 ...

  5. golang sync.Mutex 互斥锁 使用实例

    实例: var mutex sync.Mutex //互斥锁 func printer(str string){mutex.Lock() //加锁defer mutex.Unlock() //解锁fo ...

  6. golang中的互斥锁

    简介 每个资源都对应一个可称为"互斥锁"的标记,这个标记用来保证在任意时刻,只有一个协程(线程)访问该资源.其他的协程只能等待 由标准库sync中的Mutex结构体类型表示. sy ...

  7. Golang——死锁、互斥锁、读写锁的实现

    死锁: 什么是锁呢?就是某个协程(线程)在访问某个资源时先锁住,防止其它协程的访问,等访问完毕解锁后其他协程再来加锁进行访问.这和我们生活中加锁使用公共资源相似,例如:公共卫生间. 死锁是指两个或两个 ...

  8. 原子操作和互斥量的区别

    原子操作和互斥量的区别 原子操作和互斥锁都是并发编程中常见的技术. 原子操作 原子操作就是操作过程中不能被中断的过程,在针对某个值得原子操作,在被进行的过程中CPU绝对不会再进行其他的针对该值的操作. ...

  9. Linux并发与竞争介绍(原子操作、自旋锁、信号量、互斥体)

    目录 并发与竞争 并发与竞争简介 保护内容是什么 原子操作 原子操作简介 原子整形操作API函数(atomic_t 结构体) 原子位操作API 函数 自旋锁 自旋锁简介 自旋锁API函数 线程与线程 ...

  10. Linux C/C++ 并发下的技术方案(互斥锁、自旋锁、原子操作)

    前言 线程:是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务.在Uni ...

最新文章

  1. 安防行业巨头都是如何布局无人机的?
  2. 用U盘破除XP管理员密码
  3. ViSP中AprilTag的实时识别与定位
  4. LinkExtractor
  5. kickstart 为 rhel5 创建 ext4 分区
  6. leetcode--single number.
  7. BIM工程信息管理系统-EF实体框架数据操作基类
  8. 阿里程序员双11加班吃什么?
  9. 升级到jdk1.8后 sun/io/CharToByteConverter错误及处理
  10. VMware Workstation 15 解锁Mac OS安装
  11. 模因(meme)收集
  12. html获取屏幕的高度,js如何获取屏幕高度
  13. PHP为什么是最好的编程语言?
  14. 前端学习从入门到高级全程记录之31(JavaScript高级3)
  15. GOME-2 SIF 数据链接
  16. 在spring.io网下下载的快速入门项目,导进去pom.xml文件爆红,报:Non-resolvable parent POM for com.tc:demo:0.0.1-SNAPSHOT: F
  17. 【RHCSA】Linux中执行命令
  18. 超入门级-基于中值滤波处理ECG信号的基线漂移-Python-MIT-BIH数据集
  19. 别在@官方加国旗啦,3分钟30行Python代码帮你搞定!还加鸡腿,加IPhone11!
  20. Linux服务架设篇--traceroute命令

热门文章

  1. linux检查apr,Linux上安装APR
  2. 解决wampserver server offline
  3. 社会学概论试题库【1】
  4. 思科VLAN Trunk
  5. 键盘按钮KeyCode使用案例
  6. 数据字典chm制作教程
  7. Android开发书籍推荐
  8. 亚马逊aws申请ses邮件推送攻略
  9. 坚持学习,坚持阅读,坚持思考
  10. linux shell 脚本手动执行没问题,但在任务计划中执行有问题