如何理解Go运行时中的Mutex

技术如何理解Go运行时中的Mutex这篇文章主要讲解了“如何理解Go运行时中的Mutex”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解Go运行时中的Mutex”吧

本文主要讲解“如何在Go Runtime中理解互斥体”,简单明了,易学易懂。请跟随边肖的思路,一起学习和学习“如何在Go Runtime中理解互斥体”。

同步。互斥是高级同步原语,是广大Go开发者开发应用的数据结构。现在它的内部实现逻辑很复杂,包括自旋和饥饿处理逻辑。它的底层在运行时使用一些低级的函数和一些原子方法。

运行时中的互斥体是在运行时中使用互斥体的同步原语。它提供了旋转和等待队列,但没有解决饥饿状态。此外,它的实现与sync不同。互斥体它不以方法的方式提供锁定/解锁,而是提供锁定/解锁功能,实现请求锁定和释放锁定。

今年年初,Dan Scales在运行时给锁增加了静态锁定等级的功能。他在运行时为独立于架构的锁定义了等级,并在运行时定义了锁的一些部分顺序(在这个锁之前允许持有哪些锁)。这是运行时锁的一个很大的变化,但遗憾的是没有设计文档来详细描述这个函数的设计。您可以通过提交注释(#0a820007)和代码中的注释来找出运行时内部锁的代码更改。

本质上,这个功能是用来检查锁定顺序是否按照文档设计顺序执行的。如果有任何违反设置顺序的情况,可能会发生死锁。因为缺少准确的文档,而且这个函数主要是用来检查运行时锁的执行顺序的,我在本文中就将这个逻辑抹掉。要在实际的Go运行时启动该检查,您需要设置变量GOEXPERIMENT=staticlockranking。

然后我们来看看互斥在运行时的数据结构的定义和锁/解锁的实现。

运行时mutex数据结构

运行时的互斥数据结构非常简单,如下所示,在runtime2.go中定义:

typemexcstruct { lockRankStruct//futex-basedimplitstatistsuite 32 key,//而sema-basedimpliasm * waim。//usettobaunion,butunisBreakRecisegc.keyuintpr }如果没有启用锁排序,lockrankstruct实际上是一个空结构:

TypelockRankStructstruct{}那么对于运行时的互斥来说,最重要的就是关键字段。这个字段对于不同的架构有不同的含义。

对于dragonfly、freebsd和linux架构,mutex将使用基于Futex的实现,key是uint32的值。Linux提供的Futex(快速用户空间互斥体)用于在用户空间建立锁和信号量。Go runtime封装了两种睡眠和唤醒当前线程的方法:

Futex sleep (addr uint32,valuint32,nsint64):原子操作` ifaddr==val {sleep} `。

Futex唤醒(addr * uint32,cntuint32):最多在addr唤醒线程cnt次。

对于其他架构,如aix、darwin、netbsd、openbsd、plan9、solaris和windows,mutex将使用基于sema的实现,关键是M * waitm。Go runtime封装了三种创建信号量和睡眠/唤醒的方法:

Func semacreate(mp *m):创建信号量。

Func semasleep(ns int64) int32:请求一个信号量,如果没有请求,它将休眠一段时间。

Funcsem唤醒(mp * m):唤醒mp。

基于这两种实现,分别有不同的lock和unl。

ock方法的实现,主要逻辑都是类似的,所以接下来我们只看基于Futex的lock/unlock。

请求锁lock

如果不使用lock ranking特性,lock的逻辑主要是由lock2实现的。

func lock(l *mutex) {     lockWithRank(l, getLockRank(l)) } func lockWithRank(l *mutex, rank lockRank) {     lock2(l) } func lock2(l *mutex) {     // 得到g对象     gp := getg()     // g绑定的m对象的lock计数加1     if gp.m.locks < 0 {         throw("runtime&middot;lock: lock count")     }     gp.m.locks++     // 如果有幸运光环,原来锁没有被持有,一把就获取到了锁,就快速返回了     v := atomic.Xchg(key32(&l.key), mutex_locked)     if v == mutex_unlocked {         return     }     // 否则原来的可能是MUTEX_LOCKED或者MUTEX_SLEEPING     wait := v     // 单核不进行spin,多核CPU情况下会尝试spin     spin := 0     if ncpu > 1 {         spin = active_spin     }          for {         // 尝试spin,如果锁已经释放,尝试抢锁         for i := 0; i < spin; i++ {             for l.key == mutex_unlocked {                 if atomic.Cas(key32(&l.key), mutex_unlocked, wait) {                     return                 }             }             // PAUSE             procyield(active_spin_cnt)         }         // 再尝试抢锁, rescheduling.         for i := 0; i < passive_spin; i++ {             for l.key == mutex_unlocked {                 if atomic.Cas(key32(&l.key), mutex_unlocked, wait) {                     return                 }             }             osyield()         }         // 再尝试抢锁,并把key设置为mutex_sleeping,如果抢锁成功,返回         v = atomic.Xchg(key32(&l.key), mutex_sleeping)         if v == mutex_unlocked {             return         }                  // 否则sleep等待         wait = mutex_sleeping         futexsleep(key32(&l.key), mutex_sleeping, -1)     } }

unlock

如果不使用lock ranking特性,unlock的逻辑主要是由unlock2实现的。

func unlock(l *mutex) {     unlockWithRank(l) } func unlockWithRank(l *mutex) {     unlock2(l) } func unlock2(l *mutex) {     // 将key的值设置为mutex_unlocked     v := atomic.Xchg(key32(&l.key), mutex_unlocked)     if v == mutex_unlocked {         throw("unlock of unlocked lock")     }     // 如果原来有线程在sleep,唤醒它     if v == mutex_sleeping {         futexwakeup(key32(&l.key), 1)     }     //得到当前的goroutine以及和它关联的m,将锁的计数减1     gp := getg()     gp.m.locks--     if gp.m.locks < 0 {         throw("runtime&middot;unlock: lock count")     }     if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack         gp.stackguard0 = stackPreempt     } }

总体来说,运行时的mutex逻辑还不太复杂,主要是需要处理不同的架构的实现,它休眠唤醒的对象是m,而sync.Mutex休眠唤醒的对象是g。

感谢各位的阅读,以上就是“如何理解Go运行时中的Mutex”的内容了,经过本文的学习后,相信大家对如何理解Go运行时中的Mutex这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/42854.html

(0)

相关推荐

  • 如何理解公共MySQL的数据库服务器层

    技术如何理解公共MySQL的数据库服务器层如何理解公共MySQL的数据库服务器层,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。  MySQL插件式存储引擎是

    攻略 2021年11月9日
  • 支付宝申请商家二维码,怎么开通支付宝商家收钱码

    技术支付宝申请商家二维码,怎么开通支付宝商家收钱码需要开通支付宝商家收钱码支付宝申请商家二维码,需要自己是支付宝商家,方法如下: 1、首先在手机上找到并打开支付宝APP客户端。 2、然后在支付宝页面中选择红色箭头所指处的

    生活 2021年10月24日
  • Python怎样爬取上万条大众点评数据

    技术Python怎样爬取上万条大众点评数据今天就跟大家聊聊有关Python怎样爬取上万条大众点评数据,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。中国的快餐市场竞

    攻略 2021年10月26日
  • rman duplicate出现PLS-00201错误怎么办

    技术rman duplicate出现PLS-00201错误怎么办小编给大家分享一下rman duplicate出现PLS-00201错误怎么办,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这

    攻略 2021年11月17日
  • 如何理解Java快速开发平台J-Hi

    技术如何理解Java快速开发平台J-Hi如何理解Java快速开发平台J-Hi,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。J-HI是什么J-HI是一款J

    攻略 2021年11月21日
  • C++构造函数怎么用

    技术C++构造函数怎么用这篇文章给大家分享的是有关C++构造函数怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.作用一种特殊类型的方法,在每次实例化对象时运行2.代码举例2.1 示例1

    攻略 2021年11月21日