互斥锁:没有读锁写锁之分,同一时刻,只能有一个gorutine获取一把锁

数据结构设计:

  1. type Mutex struct {
  2.   state int32 // 将一个32位整数拆分为 当前阻塞的goroutine数(30位)|唤醒状态(1位)|锁状态(1位) 的形式,来简化字段设计
  3.   sema uint32 // 信号量
  4. }
  5.  
  6. const (
  7.   mutexLocked = << iota // 0001 含义:用最后一位表示当前对象锁的状态,0-未锁住 1-已锁住
  8.   mutexWoken // 0010 含义:用倒数第二位表示当前对象是否被唤醒 0-唤醒 1-未唤醒
  9.   mutexWaiterShift = iota // 2 含义:从倒数第二位往前的bit位表示在排队等待的goroutine数
  10. )

关键函数设计:

lock函数:

  1. //获取锁,如果锁已经在使用,则会阻塞一直到锁可用
  2. func (m *Mutex) Lock() {
  3. // m.sate == 0时说明当前对象还没有被锁住过,进行原子操赋值作操作设置mutexLocked状态,CompareAnSwapInt32返回true
  4. // m.sate != 1时刚好相反说明对象已被其他goroutine锁住,不会进行原子赋值操作设置,CopareAndSwapInt32返回false
  5. if atomic.CompareAndSwapInt32(&m.state, , mutexLocked) {
  6. if race.Enabled {
  7. race.Acquire(unsafe.Pointer(m))
  8. }
  9. return
  10. }
  11.  
  12. awoke := false
  13. iter :=
  14. for {
  15. old := m.state // 保存对象当前锁状态
  16. new := old | mutexLocked // 保存对象即将被设置成的状态
  17. if old&mutexLocked != { // 判断对象是否处于锁定状态
  18.       if runtime_canSpin(iter) { // 判断当前goroutine是否可以进入自旋锁
  19. if !awoke && old&mutexWoken == && old>>mutexWaiterShift != &&
  20. atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
  21. awoke = true
  22. }
  23. runtime_doSpin() // 进入自旋锁后当前goroutine并不挂起,仍然在占用cpu资源,所以重试一定次数后,不会再进入自旋锁逻辑
  24. iter++
  25. continue
  26. }
  27. // 更新阻塞goroutine的数量
  28. new = old + <<mutexWaiterShift //new = 2
  29. }
  30. if awoke {
  31. if new&mutexWoken == {
  32. panic("sync: inconsistent mutex state")
  33. }
  34.        // 设置唤醒状态位0
  35. new &^= mutexWoken
  36. }
  37. if atomic.CompareAndSwapInt32(&m.state, old, new) {
  38. if old&mutexLocked == { // 如果对象原本不是锁定状态,对象已经获取到了锁
  39. break
  40. }
  41. // 如果对象原本就是锁定状态,挂起当前goroutine,进入休眠等待状态
  42. runtime_Semacquire(&m.sema)
  43. awoke = true
  44. iter =
  45. }
  46. }
  47.  
  48. if race.Enabled {
  49. race.Acquire(unsafe.Pointer(m))
  50. }
  51. }

再来看看unlock函数,终于可以来点轻松的了

  1. func (m *Mutex) Unlock() {
  2. if race.Enabled {
  3. _ = m.state
  4. race.Release(unsafe.Pointer(m))
  5. }
  6.  
  7. // 改变锁的状态值
  8. new := atomic.AddInt32(&m.state, -mutexLocked)
  9. if (new+mutexLocked)&mutexLocked == { // 如果原来锁不是锁定状态,报错
  10. panic("sync: unlock of unlocked mutex")
  11. }
  12.  
  13. old := new
  14. for {
  15. // 1. 如果没有阻塞的goroutine直接返回
  16. // 2. 如果已经被其他goroutine获取到锁了直接返回
    // 需要说明的是为什么是old&(mutexLocked|mutexWoken)而不是old&mutexLocked
    // 首先如果是old&mutexLocked的话,那锁就没法释放了
    // 最主要的一点是lock时进入自旋锁逻辑后,goroutine持有的对象状态会设置为mutexLocked|mutexWoken
    // 这种情况让再去解锁后mutexWaiterShift数就会出现不一致情况
  17. if old>>mutexWaiterShift == || old&(mutexLocked|mutexWoken) != {
  18. return
  19. }
  20. // 更新阻塞的goroutine个数
  21. new = (old - <<mutexWaiterShift) | mutexWoken
  22. if atomic.CompareAndSwapInt32(&m.state, old, new) {
  23. // 通知阻塞的goroutine
  24. runtime_Semrelease(&m.sema)
  25. return
  26. }
  27. old = m.state
  28. }
  29. }

总结:

一、互斥效果实现方式

  1. 当前goroutine进入自旋锁逻辑等待中

  2. 挂起当前goroutine等待其他goroutine解锁通知,通过信号量实现

二、锁数据结构设计十分精简

goroutine数(30位)|唤醒状态(1位)|锁状态(1位)

golang mutex互斥锁分析的更多相关文章

  1. RWLock——一种细粒度的Mutex互斥锁

    RWMutex -- 细粒度的读写锁 我们之前有讲过 Mutex 互斥锁.这是在任何时刻下只允许一个 goroutine 执行的串行化的锁.而现在这个 RWMutex 就是在 Mutex 的基础上进行 ...

  2. Go 标准库 —— sync.Mutex 互斥锁

    Mutex 是一个互斥锁,可以创建为其他结构体的字段:零值为解锁状态.Mutex 类型的锁和线程无关,可以由不同的线程加锁和解锁. 方法 func (*Mutex) Lock func (m *Mut ...

  3. C# Mutex互斥锁

    Mutex 构造函数 (Boolean, String, Boolean) public Mutex ( bool initiallyOwned, string name, out bool crea ...

  4. C# mutex互斥锁构造

    概念 Mutext 出现的比monitor更早,而且传承自COM,当然,waitHandle也是它的父类,它继承了其父类的功能,有趣的是Mutex的脾气非常的古怪,它 允许同一个线程多次重复访问共享区 ...

  5. 【转】【C#】【Thread】Mutex 互斥锁

    Mutex:互斥(体) 又称同步基元. 当创建一个应用程序类时,将同时创建一个系统范围内的命名的Mutex对象.这个互斥元在整个操作系统中都是可见的.当已经存在一个同名的互斥元时,构造函数将会输出一个 ...

  6. Golang 读写锁RWMutex 互斥锁Mutex 源码详解

    前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...

  7. 互斥锁Mutex与信号量Semaphore的区别

    转自互斥锁Mutex与信号量Semaphore的区别 多线程编程中,常常会遇到这两个概念:Mutex和Semaphore,两者之间区别如下: 有人做过如下类比: Mutex是一把钥匙,一个人拿了就可进 ...

  8. 探索互斥锁 Mutex 实现原理

    Mutex 互斥锁 概要描述 mutex 是 go 提供的同步原语.用于多个协程之间的同步协作.在大多数底层框架代码中都会用到这个锁. mutex 总过有三个状态 mutexLocked: 表示占有锁 ...

  9. golang RWMutex读写锁分析

    RWMutex:是基于Mutex实现的读写互斥锁,一个goroutine可以持有多个读锁或者一个写锁,同一时刻只能持有读锁或者写锁 数据结构设计: type RWMutex struct { w Mu ...

随机推荐

  1. Scrollview回弹效果自定义控件

    滚动回弹效果分析: 首先,创建一个类,继承scrollview,重写ontouch事件,实现伸缩回弹效果. [scroollview节点下只能有一个子节点,这个子节点就是我们要移动的view布局]   ...

  2. 2017年试试Web组件化框架Omi

    Open and modern framework for building user interfaces. Omi的Github地址https://github.com/AlloyTeam/omi ...

  3. 用 Lua 实现一个微型虚拟机-基本篇

    用 Lua 实现一个微型虚拟机-基本篇 目录 介绍 机器指令模拟 最终核心代码 虚拟机内部状态可视化 完整项目代码 后续计划 参考 介绍 在网上看到一篇文章 使用 C 语言实现一个虚拟机, 这里是他的 ...

  4. SmartRoute之远程接口调用和负载

    基于接口的调用远比基于基础消息交互来得更简单和便于维护,特别在业务展现上,接口作为业务表现更适合其便利性.为了让SmartRoute更适合业务应用集成,在新的一年开始SmartRoute集成了远程接口 ...

  5. AspNet Identity 和 Owin 谁是谁

    英文原文:http://tech.trailmax.info/2014/08/aspnet-identity-and-owin-who-is-who/ 最近我发现Stackoverflow上有一个非常 ...

  6. OSGI框架中通过BundleContext对象对服务的注册与引用

    BundleActivator 在每个Bundle新建时都会默认生成Activator类,该类实现了BundleActivator类,实现了其start()和stop()两个方法 BundleCont ...

  7. int与integer的区别(基本数据类型与引用数据类型)

    一.先说说int与integer的区别 int 是基本数据类型,默认值为0,不需要进行实例化 integer 是引用数据类型,是int的封装类型,默认值为null,创建该类型需要进行实例化. 基本数据 ...

  8. c#访问数据库的两种方法以及事务的两种方法

    //2015/07/03 using System; using System.Collections.Generic; using System.Linq; using System.Text; u ...

  9. phpcms 列表项 内容项

    根据上一篇内容继续 首页替换完成后 接下来替换列表页 首先把列表的静态网页放入相应模板的content文件夹下,并改名为 list.html 并且创建栏目时选择下面一项 同样,头尾去掉,利用{temp ...

  10. Python 接口测试(三)

    四:python接口之http请求 python的强大之处在于提供了很多的标准库以及第三库,本文介绍urllib 和第三库的requests. Urllib 定义了很多函数和类,这些函数和类能够帮助我 ...