锁 唤醒_原子操作&普通锁&读写锁

40da5c9ae1c4a43fa7c85698e50ed613.png

一:原子操作CAS(compare-and-swap)

原子操作分三步:读取addr的值,和old进行比较,如果相等,则将new赋值给*addr,他能保证这三步一起执行完成,叫原子操作也就是说它不能再分了,当有一个CPU在访问这块内容addr时,其他CPU就不能访问

func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-25
   MOVD   addr+0(FP), R3
   MOVD   old+8(FP), R4
   MOVD   new+16(FP), R5
   SYNC
   LDAR   (R3), R6
   CMP    R6, R4
   BNE    7(PC)
   STDCCC R5, (R3)
   BNE    -4(PC)
   ISYNC
   MOVD   $1, R3
   MOVB   R3, swapped+24(FP)
   RET
   MOVB   R0, swapped+24(FP)
   RET

二:普通锁

加锁(Mutex.Lock)

1:原子操作加锁:原子操作判断是否已经被加锁,如果没有加锁,原子操作加锁,直接返回,很快吗!

2:执行旋转锁:已经被加锁,判断是否可以执行旋转锁,执行旋转锁,原子判断是否可以加锁,若可以,加锁返回

3:当前G休眠等待被唤醒:在执行旋转锁期间,锁还是没释放,那就只能让当前协程休眠,等待被唤醒,当锁被释放后,当前G被唤醒继续执行

释放锁(Mutex.UnLock)

1:将加锁状态去掉,判断是否有等待的协程,如没有直接返回

2:若有等待协程,将状态设置成唤醒状态

3:唤醒一个等待协程

三:读写锁

读写锁基于普通锁实现

加写锁(RWMutex.Lock)

1:加普通锁

2:改读锁的数量readerCount -= 1 << 30

3:如果有正在读的锁,等待直到读锁完成,读写不能同时进行

释放写锁(RWMutex.UnLock)

1:改读锁的数量readerCount += 1 << 30,加锁的时候减了这么多,释放锁的时候加回来

2:如果readerCount>= 1 << 30,抛异常,释放没有加锁的锁

3:唤醒所有正在等待读的协程

4:释放普通锁

加读锁(RWMutex.RLock)

1:原子操作读锁数量加1,readerCount+=1

2:如果rederCount<0,说明有写功能正在执行,协程进入睡眠状态,等待写完之后被唤醒

3:如果没有正在执行的写锁,就完事了,整个加锁操作就只执行了一个原子操作,还是很快的

释放读锁(RWMutex.RUnLock)

1:原子操作读锁数量减1

2:如果读锁数量==-1,或==-1 << 30,说明释放了一个没有加读锁的锁,或者释放了一个正在写的锁,直接报错

3:如果有正在等待的写锁,唤醒它,否则整个释放读锁也就执行了一个原子操作

所以说,锁是基于原子操作的,原子操作保证了数据的一致性,读写锁基于普通锁来实现,对于一个写少读多的程序来说,读写锁会比普通锁快很多

加锁原理

1:先是CAS的方式尝试获取锁,如果获取到了,就锁住,并继续执行被锁住的代码,然后在释放锁

2:CAS没有拿到锁,就只能等待了,比如有10个协程(G)在等这个待锁,go并不是一把锁创建一个队列,而是默认创建251个队列,通过hash的方式将G加入队列,确保等待同一把锁的G在同一个队列,然后将当前G执行上下文信息保存到G.sched,下次就可以继续从这里执行,这样这个等待的G就这样被扔到队列中了,而不是将这个G状态改成等待状态等待被唤醒,G去睡觉了,P还得继续执行,于是会找一个P,继续执行

解锁原理

1:通过锁定位到对应的队列,所有等待这把锁的G都在这个队列中,查找是否有等待的G,没有就返回

2:有就将G状态改成可运行,并加入到运行队列,等待被调度

关于G调度请看我的这篇文章:go并发调度原理学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值