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

相关推荐

  • [bzoj2303][Apio2011]方格染色

    技术[bzoj2303][Apio2011]方格染色 [bzoj2303][Apio2011]方格染色Sam和他的妹妹Sara有一个包含n×m个方格的表格。她们想要将其的每个方格都染成红色或蓝色。
    出于

    礼包 2021年11月25日
  • Java中有哪些Spring面试题

    技术Java中有哪些Spring面试题本篇内容主要讲解“Java中有哪些Spring面试题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中有哪些Spring面试题”吧

    攻略 2021年10月29日
  • ThinkPHP3.2如何实现中英文切换

    技术ThinkPHP3.2如何实现中英文切换这篇文章主要介绍了ThinkPHP3.2如何实现中英文切换,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1.

    攻略 2021年11月19日
  • http工具类 james

    技术http工具类 james http工具类 jamespackage com.shsnc.south.znpact.server.util;import com.shsnc.south.znpact

    礼包 2021年11月15日
  • VB.NET转换形态的方法有哪些

    技术VB.NET转换形态的方法有哪些这篇文章将为大家详细讲解有关VB.NET转换形态的方法有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。VB.NET经过长时间的发展,很多用户都很

    攻略 2021年12月1日
  • 权限设置在哪里,设置应用管理怎么开权限

    技术权限设置在哪里,设置应用管理怎么开权限工具/原料:小米6手机权限设置在哪里、手机管家 小米6手机应用管理权限开启操作步骤如下: 1、在手机桌面找到【手机管家】,点击进入【手机管家】;
    2、在【手机管家】界面,

    生活 2021年10月20日