IT评测·应用市场-qidao123.com

标题: golang单机锁实现 [打印本页]

作者: 用多少眼泪才能让你相信    时间: 6 天前
标题: golang单机锁实现
1、锁的概念引入

起首,为什么必要锁?
在并发编程中,多个线程或历程可能同时访问和修改同一个共享资源(比方变量、数据结构、文件)等,若不引入合适的同步机制,会引发以下问题:
因此,我们必要一把锁,来保证同一时间只有一个人能写数据,确保共享资源在并发访问下的正确性和一致性。
在这里,引入两种常见的并发控制处理机制,即乐观锁悲观锁
针对差别的场景必要采取因地制宜的策略,比较乐观锁与悲观所,它们的优缺点显而易见:
策略优点缺点乐观锁不必要实际上锁,性能高若冲突时,必要重新进行操作,多次重试可能会导致性能降落明显悲观锁访问数据一定必要持有锁,保证并发场景下的数据正确性加锁期间,其他等候锁的线程必要被阻塞,性能低2、Sync.Mutex

Go对单机锁的实现,考虑了实际情况中协程对资源竞争程度的变革,制定了一套锁升级的过程。具体方案如下:
乐观转向悲观的判定规则如下,满足其中之一即发生转变:
除此之外,为了防止被阻塞的协程等候过长时间也没有获取到锁,导致用户的整体体验降落,引入了饥饿的概念:
饥饿模式与正常模式的转变规则如下:
接下来步入源码,观看具体的实现。
2.1、数据结构

位于包sync/mutex.go中,对锁的定义如下:
  1. type Mutex struct {
  2.         state int32
  3.         sema  uint32
  4. }
复制代码
将state看作一个二进制字符串,它存储信息的规则如下:
  1. const (
  2.         mutexLocked = 1 << iota // mutex is locked
  3.         mutexWoken
  4.         mutexStarving
  5.         mutexWaiterShift = iota
  6.         starvationThresholdNs = 1e6 //饥饿阈值
  7. )
复制代码
(2)进入尝试获取锁的循环中,两个if表现:
<ul>若锁处于上锁状态,并且不处于饥饿状态中,并且当前的协程允许继续自旋下去(非单核CPU、自旋次数>mutexWaiterShift == 0 {                                        throw("sync: inconsistent mutex state")                                }                //将要更新的信号量                                delta := int32(mutexLocked - 1mutexWaiterShift == 1 {                                        delta -= mutexStarving                                }                                atomic.AddInt32(&m.state, delta)                                break                        }                        awoke = true                        iter = 0        //....                } else {                        //...                }[/code]从阻塞中唤醒,起首计算一些协程的阻塞时间,以及当前的最新锁状态。
锁处于饥饿模式:那么当前协程将直接获取锁,当前协程是因为饥饿模式被唤醒的,不存在其他协程抢占锁。于是更新信号量,将记录阻塞协程数-1,将锁的上锁态置1。若当前从饥饿模式唤醒的协程,等候时间已经不到1ms了大概是最后一个等候的协程,那么将将锁从饥饿模式转化为正常模式。至此,获取成功,退出函数。
否则,只是普通的随机唤醒,于是开始尝试进行抢占,回到步骤1。
2.4、释放锁Unlock()
  1. func (m *Mutex) Lock() {
  2.         if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
  3.                 return
  4.         }
  5.         m.lockSlow()
  6. }
复制代码
通过原子操作,直接将锁的mutexLocked标识置为0。若置0后,锁的状态不为0,那就说明存在必要获取锁的协程,步入unlockSlow。
2.5、unlockSlow()

[code]func (m *Mutex) unlockSlow(new int32) {        if (new+mutexLocked)&mutexLocked == 0 {                fatal("sync: unlock of unlocked mutex")        }        if new&mutexStarving == 0 {                old := new                for {                        if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {                                return                        }                        new = (old - 1




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4