golang mutex两加两解助你实现高并发控制

简介: golang mutex两加两解助你实现高并发控制

天下之事常成于困约,而败于奢靡。——陆游


1 前言



互斥锁是并发程序中对共享资源进行访问控制的主要手段,因此Go设计者提供了非常简单易用的Mutex供我们使用,接下来我们从源码剖析实现原理,又不会过分纠结于实现细节。


2 Mutex数据结构



2.1 结构体定义


type Mutex struct {
    state int32
    sema  uint32
}
  • state表示互斥锁的状态,比如是否被锁定等
  • sema表示信号量,协程阻塞等待该信号量,解锁的协程释放信号量从而唤醒等待信号量的协程。


state是32位的整型变量,内部实现时把该变量分成四份,用于记录Mutex的四种状态,如下图:

  • Locked: 表示该Mutex是否已被锁定,0:没有锁定 1:已被锁定。
  • Woken: 表示是否有协程已被唤醒,0:没有协程唤醒 1:已有协程唤醒,正在加锁过程中。
  • Starving:表示该Mutex是否处于饥饿状态, 0:没有饥饿 1:饥饿状态,说明有协程阻塞了超过1ms。
  • Waiter: 表示阻塞等待锁的协程个数,协程解锁时根据此值来判断是否需要释放信号量。


协程之间抢锁实际上是抢给Locked赋值的权利,能给Locked字段置为1,就说明抢锁成功。抢不到的话就阻塞等待sema信号量,一旦持有锁的协程解锁,等待的协程会依次被唤醒。

Woken和Starving主要用于控制协程间的抢锁过程。


2.2 方法


Mutext对外提供两个方法:

  • Lock() : 加锁方法
  • Unlock(): 解锁方法


下面我们分析一下加锁和解锁的过程,加锁分成功和失败两种情况,成功的话直接获取锁,失败后当前协程被阻塞,同样,解锁时跟据是否有阻塞协程也有两种处理。


3 加锁



3.1 简单加锁


假如当前只有一个goroutine在加锁,没有其他goroutine干扰,那么加锁过程如下图:

加锁过程会去判断Locked标志位是否为0,如果是0则把Locked位置1,代表加锁成功。从上图可见,加锁成功后,只是Locked位置1,其他状态位没发生变化。


3.2 加锁被阻塞


假如加锁时,锁已经被其他goroutine占用了,此时加锁过程如下图:

从上图可看到,当goroutine-B对一个已被占用的锁再次加锁时,Waiter计数器增加1,此时goroutine-B将被阻塞,直到Locked值变为0后才会被唤醒。


4 解锁



4.1 简单解锁


假如解锁时,没有其他goroutine阻塞,此时解锁过程如下图:

由于没有其他goroutine阻塞等待加锁,所以此时解锁时只需要把Locked位置为0即可,不需要释放信号量。


4.2 解锁并唤醒协程


假如解锁时,有1个或多个goroutine阻塞,此时解锁过程如下图:


goroutine-A解锁过程分为两个步骤,一是把Locked位置为0,二是查看到Waiter>0,所以释放一个信号量,唤醒一个阻塞的goroutine,被唤醒的goroutine-B把Locked位置为1,于是goroutine-B获得锁。


5 小结



目前没有涉及到大量源码,因为mutex的源码本身比较多,由于篇幅关系这里只是提取了mutex的实现原理,但是还有部分细节准备在下次分享中继续讨论,比如自旋,工作模式以及饥饿模式等。


6 关注公众号



微信公众号:堆栈future

相关文章
|
2天前
|
安全 Go 持续交付
深入Golang之Mutex
深入Golang之Mutex
34 0
|
2天前
|
Go
浅谈Golang并发控制WaitGroup
浅谈Golang并发控制WaitGroup
26 0
|
7月前
|
安全 Go 数据安全/隐私保护
Golang 语言中基础同步原语 Mutex 和 RWMutex 的区别
Golang 语言中基础同步原语 Mutex 和 RWMutex 的区别
56 0
|
10月前
|
Go 调度
golang mutex一旋二饿三唤醒机制
golang mutex一旋二饿三唤醒机制
|
存储 安全 Go
深入 golang 之 ---goroutine 并发控制与通信
深入 golang 之 ---goroutine 并发控制与通信
|
Go 调度
golang 系列: mutex 讲解
Go 号称是为了高并发而生的,在高并发场景下,势必会涉及到对公共资源的竞争。当对应场景发生时,我们经常会使用 mutex 的 Lock() 和 Unlock() 方法来占有或释放资源。虽然调用简单,但 mutex 的内部却涉及挺多的。今天,就让我们好好研究一下。
348 0
golang 系列: mutex 讲解
|
2天前
|
监控 算法 Go
Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
52 0
|
2天前
|
分布式计算 Java Go
Golang深入浅出之-Go语言中的分布式计算框架Apache Beam
【5月更文挑战第6天】Apache Beam是一个统一的编程模型,适用于批处理和流处理,主要支持Java和Python,但也提供实验性的Go SDK。Go SDK的基本概念包括`PTransform`、`PCollection`和`Pipeline`。在使用中,需注意类型转换、窗口和触发器配置、资源管理和错误处理。尽管Go SDK文档有限,生态系统尚不成熟,且性能可能不高,但它仍为分布式计算提供了可移植的解决方案。通过理解和掌握Beam模型,开发者能编写高效的数据处理程序。
140 1
|
2天前
|
缓存 测试技术 持续交付
Golang深入浅出之-Go语言中的持续集成与持续部署(CI/CD)
【5月更文挑战第5天】本文介绍了Go语言项目中的CI/CD实践,包括持续集成与持续部署的基础知识,常见问题及解决策略。测试覆盖不足、版本不一致和构建时间过长是主要问题,可通过全面测试、统一依赖管理和利用缓存优化。文中还提供了使用GitHub Actions进行自动化测试和部署的示例,强调了持续优化CI/CD流程以适应项目需求的重要性。
54 1