Go 并发可视化解释 - sync.Mute

在学习 Go 编程语言时,您可能会遇到这句著名的格言:“不要通过共享内存来进行通信;相反,通过通信来共享内存。” 这句话构成了 Go 强大并发模型的基础,其中通道(channels)作为协程之间的主要通信工具。然而,虽然通道是管理并发的多功能工具,但错误地假设我们应该始终用通道替换传统的锁定机制,如 Mutex,是一个错误的观念。在某些情况下,使用 Mutex 不仅恰当,而且比通道更有效。

在我的 Go 并发可视化系列中,今天我将通过视觉方式来解释 sync.Mutex

Golang 基础

场景

想象一下,有四位 Gopher 自行车手每天骑车上班。他们都需要在到达办公室后洗个澡,但办公室只有一个浴室。为了防止混乱,他们确保一次只能有一个人使用浴室。这种独占式访问的概念正是 Go Mutex(互斥锁)的核心。

bf34c38a3f6fc9ab2d159ffe05b90bbd.png

每天早上在办公室洗澡对自行车手和跑步者来说是一个小小的竞争。

普通模式

今天最早到达的是 Stringer。当他来的时候,没有人在使用浴室,因此他可以立即使用浴室。

对一个未加锁的 Mutex 调用 Lock() 会立即成功。

片刻后,Partier 到了。Partier 发现有人在使用浴室,但他不知道是谁,也不知道什么时候会结束使用。此时,他有两个选择:站在浴室前面(主动等待),或者离开并稍后再回来(被动等待)。按 Go 的术语,前者被称为“自旋”(spinning)。自旋的协程会占用 CPU 资源,增加了在锁定可用时获取 Mutex 的机会,而无需进行昂贵的上下文切换。然而,如果 Mutex 不太可能很快可用,继续占用 CPU 资源会降低其他协程获取 CPU 时间的机会。

从版本 1.21 开始,Golang 允许到达的协程自旋一段时间。如果在指定时间内无法获取 Mutex,它将进入休眠状态,以便其他协程有机会运行。

14dea1bc70160aedd59d555282387957.png

到达的协程首先自旋,然后休眠。

Candier 到了。就像 Partier 一样,她试图获取浴室。

1713dbf0667141489a7a37329b5932b3.png

因为她刚到,如果 Stringer 很快释放浴室,她就有很大的机会在被动等待之前获取它。这被称为普通模式。

普通模式的性能要好得多,因为协程可以连续多次获取 Mutex,即使有阻塞的等待者。

802c169f355683c6be50192d2c2b88c1.png
1*GJ7OW0_8z_8QjXPa2cFxPw.png

go/src/sync/mutex.go at go1.21.0 · golang/go · GitHub[1]

新到达的协程在争夺所有权时具有优势

饥饿模式

Partier 回来了。由于他等待的时间很长(超过 1 毫秒),他将尝试以饥饿模式获取浴室。当 Swimmer 来时,他注意到有人饿了,他不会尝试获取浴室,也不会自旋。相反,他会排队在等待队列的尾部。

在这种饥饿模式下,当 Candier 结束时,她会直接把浴室交给 Partier。此时没有竞争。

b37df75b60e60bc216fc9153faa06bc4.png

饥饿模式是防止尾延迟的病理情况的重要措施。

1411210b8c3edaa0fa92edf479dbc3a9.png
7d4dfe9465324ba15876a95a37811c01.png

Partier 完成了他的回合并释放了浴室。此时,只有 Swimmer 在等待,因此他将立即拥有它。Swimmer 如果发现自己是最后一个等待的人,他会将 Mutex 设置回普通模式。如果他发现自己的等待时间少于 1 毫秒,也会这样做。

最后,Swimmer 在使用浴室后释放了它。请注意,Mutex 不会将所有者从“已锁定(由 Goroutine A 锁定)”状态更改为“已锁定(由 Goroutine B 锁定)”状态。它始终会在“已锁定”到“未锁定”然后再到“已锁定”的状态之间切换。出于简洁起见,上面的图像中省略了中间状态。

展示代码!

Mutex 的实现随时间而变化,实际上,要完全理解它的实现并不容易。幸运的是,我们不必完全理解其实现就能高效使用它。如果从这篇博客中只能记住一件事,那一定是:早到的人不一定赢得比赛。相反,新到达的协程通常具有更高的机会,因为它们仍在 CPU 上运行。Golang 还尝试避免通过实现饥饿模式来使等待者被饿死。

package mainimport ("fmt""sync""time"
)func main() {wg := sync.WaitGroup{}wg.Add(4)bathroom := sync.Mutex{}takeAShower := func(name string) {defer wg.Done()fmt.Printf("%s: I want to take a shower. I'm trying to acquire the bathroom\n", name)bathroom.Lock()fmt.Printf("%s: I have the bathroom now, taking a shower\n", name)time.Sleep(500 * time.Microsecond)fmt.Printf("%s: I'm done, I'm unlocking the bathroom\n", name)bathroom.Unlock()}go takeAShower("Partier")go takeAShower("Candier")go takeAShower("Stringer")go takeAShower("Swimmer")wg.Wait()fmt.Println("main: Everyone is Done. Shutting down...")
}

正如您可能猜到的,并发代码的结果几乎总是非确定性的。

第一次

Swimmer: I want to take a shower. I'm trying to acquire the bathroom

Partier: I want to take a shower. I'm trying to acquire the bathroom

Candier: I want to take a shower. I'm trying to acquire the bathroom

Stringer: I want to take a shower. I'm trying to acquire the bathroom

Swimmer: I have the bathroom now, taking a shower

Swimmer: I'm done, I'm unlocking the bathroom

Partier: I have the bathroom now, taking a shower

Partier: I'm done, I'm unlocking the bathroom

Candier: I have the bathroom now, taking a shower

Candier: I'm done, I'm unlocking the bathroom

Stringer: I have the bathroom now, taking a shower

Stringer: I'm done, I'm unlocking the bathroom

main: Everyone is Done. Shutting down...

第二次

Swimmer: I want to take a shower. I'm trying to acquire the bathroom

Swimmer: I have the bathroom now, taking a shower

Partier: I want to take a shower. I'm trying to acquire the bathroom

Stringer: I want to take a shower. I'm trying to acquire the bathroom

Candier: I want to take a shower. I'm trying to acquire the bathroom

Swimmer: I'm done, I'm unlocking the bathroom

Partier: I have the bathroom now, taking a shower

Partier: I'm done, I'm unlocking the bathroom

Stringer: I have the bathroom now, taking a shower

Stringer: I'm done, I'm unlocking the bathroom

Candier: I have the bathroom now, taking a shower

Candier: I'm done, I'm unlocking the bathroom

main: Everyone is Done. Shutting down...

自己实现 Mutex

实现 sync.Mutex 是困难的,但使用具有缓冲的通道来实现 Mutex 却相当容易。

type MyMutex struct {ch chan bool
}func NewMyMutex() *MyMutex {return &MyMutex{// 缓冲大小必须为 1ch: make(chan bool, 1),}
}// Lock 锁定 m。
// 如果锁已被使用,调用的协程将被阻塞,直到 Mutex 可用。
func (m *MyMutex) Lock() {[m.ch](http://m.ch) <- true
}// Unlock 解锁 m。
func (m *MyMutex) Unlock() {<-m.ch
}

这篇文章通过生动的场景和可视化效果很好地解释了 Go 语言中 sync.Mutex 的工作原理,以及如何使用互斥锁来管理并发

相关系列文章

使用通信顺序进程(CSP)模型的 Go 语言通道

Go并发可视化解释 – select语句

以可视化方式解释 Go 并发 - 通道

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/85831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

6条优势,anzo capital昂首资本相信MT5替代MT4的原因

投资者都知道MT5是在MT4基础上升级换代的多资产平台&#xff0c;MT5于2010年6月首次发布。anzo capital昂首资本认为MT5将完全取代MT4&#xff0c;就像MT4取代之前版本一样&#xff0c;因为有以下6条优势&#xff1a; 一.市场深度(DOM)数据。在MT4中&#xff0c;DOM几乎没有用…

什么是生成对抗网络 (GAN)?

什么是生成对抗网络 &#xff08;GAN&#xff09;&#xff1f; 钦吉兹赛义德贝利 一、说明 GAN&#xff08;Generative Adversarial Network&#xff09;网络是一种深度学习模型&#xff0c;由两个神经网络——生成器和判别器组成。生成器负责生成虚假的数据&#xff0c;而判别…

yarn安装依赖时报错 error An unexpected error occurred:

一切起因是因为前一天安装了volta管理node&#xff0c;第二天启动项目&#xff0c; 显示error An unexpected error occurred: “https://registry.npmmirror.com/webpack-aliyun-oss/-/webpack-aliyun-oss-0.2.6.tgz: Request failed “404 Not Found””. 项目启动时发现报错…

【WSN】基于蚁群算法的WSN路由协议(最短路径)消耗节点能量研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

在 SQL Server 中,可以使用内置函数 NEWID() 来生成一个随机的 GUID(全局唯一标识符)。可以将此 GUID 作为字符串拼接到查询结果中。

以下是一个示例&#xff1a; SELECT Column1, Column2, CAST(NEWID() AS VARCHAR(36)) as random_string FROM your_table;这将在查询结果中添加一个名为 random_string 的列&#xff0c;其中包含一个随机生成的 GUID。请注意&#xff0c;由于 GUID 是随机生成的&#xff…

MySQL学习系列(9)-每天学习10个知识

目录 1. 临时表&#xff08;Temporary Tables&#xff09;的优点和缺点2. 使用GROUP BY进行聚合查询3. MySQL的索引设计4. NULL和默认值的区别和用途5. 使用LIKE和REGEXP进行模式匹配6. SQL标准的三个范式7. 视图和有效性约束8. 自连接&#xff08;Self Join&#xff09;的场景…

【虚拟化】虚拟机vcpu绑核物理机

文章目录 一、NUMA二、虚拟机xml配置解析 参考文章 第一篇&#xff1a;KVM虚拟化CPU技术总结 第二篇&#xff1a;虚机cpu和mem的配置&#xff08;cputune和numatune&#xff09; 第三篇&#xff1a;libvirt 中cpu, numa 的配置 第四篇&#xff1a;如何提高虚拟机性能&#xff1…

安全学习_开发相关_JavaEE过滤器监听器简单了解

文章目录 Web应用运行流程图 JavaEE-过滤器-Filter过滤器概述&作用过滤器相关安全测试场景 JavaEE-监听器-Listener监听器作用&#xff1a;监听器相关安全测试场景 过滤器和监听器&#xff0c;主要对安全测试有影响的是过滤器&#xff0c;监听器只是在对代码进行逻辑分析时…

民安智库(第三方社会评估调研公司)社区健康服务中心满意度提升方案

提升社区健康服务中心的满意度需要综合考虑医疗服务、员工表现、便利性和健康教育等多个方面。以下是一些提升社区健康服务中心满意度的方案和建议&#xff1a; 1. 提高医疗服务质量&#xff1a; 培训医疗专业人员&#xff0c;确保他们具备最新的医疗知识和技能。确保及时提供…

2020-2023中国高等级自动驾驶产业发展趋势研究-中国高等级自动驾驶发展近况

1.2 中国高等级自动驾驶发展近况 通过对中国高等级自动驾驶行业的观察和分析&#xff0c;亿欧汽车认为&#xff0c;除技术解决方案提供商外&#xff0c;如今的车企、政府、资本同样在产业链中扮演重要角色。此外&#xff0c;车路协同技术的发展也为高等级自动驾驶的发展提供了更…

Web自动化框架中验证码识别处理全攻略,让测试更得心应手!

前言&#xff1a; 随着Web应用程序的不断发展&#xff0c;自动化测试已成为项目开发中必不可少的一环。然而&#xff0c;验证码的出现却经常会使自动化测试变得更具挑战性。为了解决这个问题&#xff0c;我们需要一种方法来自动识别和处理验证码&#xff0c;从而提高自动化测试…

位图的实现与应用

目录 位图 实现思路 模拟实现 类模板 构造函数 set reset test twobitset 实现 成员变量 set is_once 布隆过滤器 成员变量与模板 set test 删除 位图 位图是哈希的另一种表现&#xff0c;与我们常用的 set map 的思想还是基本一样的&#xff0c;下面我们看一…

leetcode面试题0808有重复字符串的排列组合

描述 输入一个长度为 n 字符串&#xff0c;打印出该字符串中字符的所有排列&#xff0c;你可以以任意顺序返回这个字符串数组。 例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB。 数据范围&#xff1a;n<10 要求&#xff1a;空间复…

引领初创企业的数字化转型:选择适合的低代码平台

初创企业在初期各项架构都还不完善&#xff0c;对于应用程序的需求多样&#xff0c;但是又要考量成本。所以&#xff0c;低代码平台就是在综合考量成本和需求的情况下的一个突出的选择。下面我们就六个方面为您介绍&#xff1a;初创企业选择的Zoho Creator低代码平台。 1、功能…

机器学习笔记 - k-NN算法的数学表达

一、概述 所有的机器学习算法都是有假设前提的。k-NN算法的假设前提是相似的输入有相似的输出。其分类规则是对于测试输入x,在其k个最相似的训练输入中分配最常见的标签。 k-NN 的正式定义: 对于一个待测试数据。 将的个最近邻的集合表示为 。的正式定义为 ,并且。(意思就是…

mmdetection v3避坑

命令&#xff1a; python tools/test.py projects/DiffusionDet/configs/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco.py /data/zhangrui/mmdetection-master/checkpoints/diffusiondet_r50_fpn_500-proposals_1-step_crop-ms-480-800-450k_coco_202…

云计算的发展趋势和挑战

本文将探讨云计算的发展趋势和挑战&#xff0c;旨在帮助读者了解云计算的最新动态和未来发展方向。 随着信息技术的发展&#xff0c;云计算作为一种新兴的计算模式&#xff0c;已经得到了广泛的应用和认可。它通过将计算资源、存储资源和应用程序等服务通过互联网提供给用户&a…

华为OD机试真题- 阿里巴巴找黄金宝箱(IV)-2023年OD统一考试(B卷)

题目描述: 一贫如洗的樵夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0~N的箱子,每个箱子上面贴有一个数字,箱子排列成一个环,编号最大的箱子的下一个是编号为0的箱子。 请输出每个箱子贴的数字之后的第一个比它大的数,如果不存在则输出-1。 …

Linux第一次作业

一&#xff0c;作业问题&#xff1a; 二&#xff0c;问题解答&#xff1a; 1. 2.文件管理命令练习 3.vi/vim练习 3.1 3.2 3.3 3.4

产品文档-BRD、MRD和PRD

1、BRD&#xff08;Business Requirement Document&#xff09;&#xff0c;商业需求文档&#xff0c;是一份产品商业论证报告&#xff0c;基于商业目标或价值所描述的产品需求内容文档&#xff0c;是领导层进行产品研发立项决策评估的重要依据&#xff0c;是产品生命周期中最早…