如何理解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)

相关推荐

  • 如何分析Linux PIE/堆栈内存损坏漏洞CVE-2017-1000253

    技术如何进行Linux PIE/stack 内存破坏漏洞CVE-2017-1000253分析本篇文章为大家展示了如何进行Linux PIE/stack 内存破坏漏洞CVE-2017-1000253分析,内容简明扼要并且容

    2021年12月21日
  • sqoop安装部署

    技术sqoop安装部署 sqoop安装部署sqoop安装部署
    安装sqoop的前提是已经具备Java和Hadoop环境。
    下载地址:https://www.apache.org/dyn/closer.l

    礼包 2021年11月9日
  • html中script解析顺序(html的script使用方法)

    技术html中script如何使用这篇文章主要介绍“html中script如何使用”,在日常操作中,相信很多人在html中script如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”

    攻略 2021年12月17日
  • 如何理解LayaAir中的EventDispatcher类

    技术如何理解LayaAir中的EventDispatcher类这篇文章将为大家详细讲解有关如何理解LayaAir中的EventDispatcher类,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后

    攻略 2021年11月11日
  • 如何修改交易代码SE16中的结果集

    技术怎么实现事务码SE16里的结果集修改怎么实现事务码SE16里的结果集修改,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。注: 这种方法不同

    攻略 2021年12月18日
  • 抽象工厂

    技术抽象工厂 抽象工厂抽象工厂(Abstract Factory)
    Intent
    提供一个接口,用于创建相关的对象家族
    Class Diagram
    抽象工厂模式创建的是对象家族,也就是很多对象而不是一个

    礼包 2021年11月10日