16 | Semaphore:一篇文章搞懂信号量
晁岳攀
该思维导图由 AI 生成,仅供参考
你好,我是鸟窝。
在前面的课程里,我们学习了标准库的并发原语、原子操作和 Channel,掌握了这些,你就可以解决 80% 的并发编程问题了。但是,如果你要想进一步提升你的并发编程能力,就需要学习一些第三方库。
所以,在接下来的几节课里,我会给你分享 Go 官方或者其他人提供的第三方库,这节课我们先来学习信号量,信号量(Semaphore)是用来控制多个 goroutine 同时访问多个资源的并发原语。
信号量是什么?都有什么操作?
信号量的概念是荷兰计算机科学家 Edsger Dijkstra 在 1963 年左右提出来的,广泛应用在不同的操作系统中。在系统中,会给每一个进程一个信号量,代表每个进程目前的状态。未得到控制权的进程,会在特定的地方被迫停下来,等待可以继续进行的信号到来。
最简单的信号量就是一个变量加一些并发控制的能力,这个变量是 0 到 n 之间的一个数值。当 goroutine 完成对此信号量的等待(wait)时,该计数值就减 1,当 goroutine 完成对此信号量的释放(release)时,该计数值就加 1。当计数值为 0 的时候,goroutine 调用 wait 等待该信号量是不会成功的,除非计数器又大于 0,等待的 goroutine 才有可能成功返回。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
信号量(Semaphore)是一种重要的并发原语,用于控制多个goroutine同时访问多个资源。本文深入介绍了信号量的概念、P/V操作以及初始化、P操作和V操作的实现方式。通过生动的图书馆借书例子,读者可以更好地理解信号量的概念和操作方式。文章还讨论了计数信号量和二进位信号量的区别与联系,以及信号量与互斥锁的关系。此外,文章提到了在高并发场景下可能出现的饥饿问题,以及处理饥饿问题的方法。在Go官方扩展库中,信号量的实现是通过互斥锁+List实现的,其中Acquire方法是代码最复杂的一个方法。文章还总结了使用信号量时的常见错误,如请求了资源却忘记释放、释放了从未请求的资源等。总的来说,本文通过生动的例子和详细的操作方式,帮助读者快速了解了信号量的概念和实现方式,为进一步提升并发编程能力提供了基础知识。此外,文章还探讨了使用Channel实现信号量的方法以及官方扩展库信号量的优势,为读者提供了更多思考和讨论的空间。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 并发编程实战课》,新⼈⾸单¥59
《Go 并发编程实战课》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(17)
- 最新
- 精选
- 木头发芽semaphore是用Mutex和Channel通知实现的,而Mutex又依赖于go内部的信号量实现,那这个内部的信号量又是用什么实现的?
作者回复: atomic和gopark,参考runtime/sema.go
2020-12-145 - 刚子不是很理解这句话 :"一次请求多个资源,这是通过 Channel 实现的信号量所不具备的。" Channel 也可以开启多个goroutine 去请求多个资源
作者回复: 意思是通过一次调用,只能从chan中获取一个值,多个。goroutine需要调用多次才能得到n个值
2020-11-162 - Geek_2c2c44基于channel的lock实现这里, Lock和UnLock的实现互换一下顺序是不是也是可以的呢?如果寻Accquire(P)是减去信号量的规范的话, 是不是lock那里把channel写在右边会更好(从channel中拿出一个信号量)?PS:前面一节的Lock顺序和这里就是不同的
作者回复: s是的,两种方式都可以
2024-01-24归属地:浙江 - 白开d水打卡
作者回复: 👍🏻
2023-03-23归属地:北京 - Geek_a6104ereturn &semaphore{ch: make(chan struct{}, capacity)} 请问一下最后一个例子semaphore结构初始化为啥会多个capacity
作者回复: 等于你的资源数。预先确定好
2022-07-31归属地:北京 - 8.13.3.27.30打卡完成
作者回复: 👍🏻👍🏻👍🏻
2022-01-06 - 伟伟type Semaphore chan struct{} func NewSemaphore(cap int) Semaphore { return make(chan struct{}, cap) } func (s Semaphore) Acquire(n int) { for i := 0; i < n; i++ { s <- struct{}{} } } func (s Semaphore) Release(n int) { for i := 0; i < n; i++ { <-s } } func NewSemaphore2(cap int) Semaphore { sem := make(chan struct{}, cap) for i := 0; i < cap; i++ { sem <- struct{}{} } return sem } func (s Semaphore) Acquire2(n int) { for i := 0; i < n; i++ { <-s } } func (s Semaphore) Release2(n int) { for i := 0; i < n; i++ { s <- struct{}{} } }
作者回复: 唯一存在的问题是可能出现死锁。 比如信号量是10,同时有两个goroutine请求8个资源。
2020-11-236 - 虫子樱桃老师的例子里面,是通过 计算机的协程 runtime.GOMAXPROCS(0) 来模拟有限的资源(比喻例子里面的书),那么这个semaphore的场景是不是就是比较适用于请求有流量或者调用次数限制的场景呢?
作者回复: 这个更多是用ratelimiter,信号量主要并发访问n个资源的场景
2020-11-19 - Ethan Liu老师,Acquire函数为什么还会有第二个select语句?这部分逻辑是什么啊?
作者回复: 理解了ctx,也就理解select。当外部context通知取消请求时,会在检查一下当前是否请求成功了
2020-11-173 - thomas补充说明下 信号量 p/v的含义: P—— passeren,中文译为"通过",V—— vrijgeven,中文译为"释放" (因为作者是荷兰人,上面单词为荷兰语)2021-01-01120
收起评论