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

相关推荐

  • php simplexml如何修改数据

    技术php simplexml如何修改数据本篇内容介绍了“php simplexml如何修改数据”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔

    攻略 2021年10月26日
  • 塑料三角烧瓶,三角烧瓶和三角瓶的区别

    技术塑料三角烧瓶,三角烧瓶和三角瓶的区别一塑料三角烧瓶、应用领域区别:1、三角瓶多用于生物培育菌种实验,有盖。2、三角烧瓶即锥形瓶是化学实验中常见仪器,多无盖,可加热,用于化学实验。
    二、外形区别:
    3、锥形瓶一般来说

    生活 2021年10月26日
  • 答复的英文,“请尽快回复”用英文该怎样说

    技术答复的英文,“请尽快回复”用英文该怎样说“请尽快回复”的英文答复的英文:Please reply as soon as possible.reply 读法 英 [rɪplaɪ] 美 [rɪplai]
    1、作不及物

    生活 2021年10月19日
  • SQL,5)— 常用函数

    技术SQL,5)— 常用函数 SQL(5)— 常用函数1、AVG 函数
    AVG 函数返回数值列的平均值。NULL 值不包括在计算中。2、COUNT
    COUNT() 函数返回匹配指定条件的行数。COUNT

    礼包 2021年11月15日
  • 什么是91视频

    技术什么是91视频本篇内容主要讲解“什么是91视频”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是91视频”吧! 91视频是一款视频播放软件,

    攻略 2021年10月25日
  • HiveMQ是什么

    技术HiveMQ是什么小编给大家分享一下HiveMQ是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!简单介绍HiveMQ是企业级MQTT

    攻略 2021年12月10日