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

相关文章
|
7月前
|
安全 Go 持续交付
深入Golang之Mutex
深入Golang之Mutex
79 0
|
7月前
|
Go
浅谈Golang并发控制WaitGroup
浅谈Golang并发控制WaitGroup
61 0
|
1月前
|
Go 计算机视觉
在Golang高并发环境中如何进行协程同步?
在此示例中,使用互斥锁来保护对共享计数器变量 c 的访问,确保并发的 HTTP 请求不会产生数据竞争。
45 3
|
安全 Go 数据安全/隐私保护
Golang 语言中基础同步原语 Mutex 和 RWMutex 的区别
Golang 语言中基础同步原语 Mutex 和 RWMutex 的区别
103 0
golang mutex一旋二饿三唤醒机制
golang mutex一旋二饿三唤醒机制
|
存储 安全 Go
深入 golang 之 ---goroutine 并发控制与通信
深入 golang 之 ---goroutine 并发控制与通信
|
Go 调度
golang 系列: mutex 讲解
Go 号称是为了高并发而生的,在高并发场景下,势必会涉及到对公共资源的竞争。当对应场景发生时,我们经常会使用 mutex 的 Lock() 和 Unlock() 方法来占有或释放资源。虽然调用简单,但 mutex 的内部却涉及挺多的。今天,就让我们好好研究一下。
395 0
golang 系列: mutex 讲解
|
Go
Golang高并发:生产者消费者模型
Golang高并发:生产者消费者模型
1748 0