go互斥锁

打印 上一主题 下一主题

主题 1637|帖子 1637|积分 4911

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
互斥锁的定义
  1.   type Mutex struct {
  2.           state int32
  3.           sema  uint32
  4.   }
复制代码
一个 sema,背后实际上 是一个 休眠队列,可以看下上篇。
一个state,这个状态 分为4个部分。
后三位 各自代表一个状态。 前29位代表最大可等待协程的个数。
state的结构
  1.   locked  是否加锁 1加锁,0 正常   占1位
  2.   woken  是否醒来                       占1位
  3.   starving 是否饥饿模式                占1位
  4.   waiterShift 等待的数量               占29位
复制代码
底层的定义,下面看代码时候,会说明。
正常模式

加锁

假设现在来了2个g,都想加锁,但是只有一个能成功,2个都通过 atomic.CompareAndSwapInt32(lock, 0 ,1)  伪代码去更改 locked 位置。
改成功的g获取了锁,没成功的g先自旋几次,然后如果还是未获取到锁,则进入sema休眠队列。
未成功的g进入休眠队列,把waiterShift加1。
通过这个结论,看代码验证下:
  1. mutexLocked = 1 << iota // mutex is locked
  2. mutexWoken
  3. mutexStarving
  4. mutexWaiterShift = iota
  5. func (m *Mutex) Lock() {
  6.      // 先给state的最后一位 写 1
  7.         if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
  8.                 if race.Enabled {
  9.                         race.Acquire(unsafe.Pointer(m))
  10.                 }
  11.         写上了, 加锁成功,直接返回。
  12.                 return
  13.         }
  14.     // 写不上进入这个方法
  15.         m.lockSlow()
  16. }
  17. // 不是完整代码,只截取和这里相关的部分
  18. func (m *Mutex) lockSlow() {
  19.         starving := false
  20.         iter := 0
  21.         old := m.state
  22.         for {
  23.                 //  是否是饥饿模式 是否还能自旋 iter会记录自旋次数
  24.                 if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
  25.                         runtime_doSpin()
  26.                         iter++ // 自旋次数加1
  27.                         old = m.state
  28.                         continue
  29.                 }
  30.                 // 自旋一定次数后
  31.                 new := old
  32.                 // 判断是否是饥饿模式
  33.                 if old&mutexStarving == 0 {
  34.                         new |= mutexLocked
  35.                 }
  36.                 if atomic.CompareAndSwapInt32(&m.state, old, new) {
  37.                         runtime_SemacquireMutex(&m.sema, queueLifo, 1)
  38.             // 进入了休眠 ,不会执行下面的语句了。直到被唤醒
  39.                         starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs               
  40.         }
  41. }
复制代码
小结:
  1. 尝试CAS直接加锁
  2. 若无法直接获取,进行多次自旋尝试
  3. 多次尝试失败,进入sema队列休眠
复制代码
如果这个时候,再来一个:
也是同样,进入sema的休眠队列。
解锁

解锁的这个g,除了修改locked的值,还需要去判断waiterShift,有没有协程在等,如果有,要去唤醒一个协程。
看代码:
[code]func (m *Mutex) Unlock() {     // 减去1,发现state的值,不是0,说明有协程在等        new := atomic.AddInt32(&m.state, -mutexLocked)        if new != 0 {                m.unlockSlow(new)        }}func (m *Mutex) unlockSlow(new int32) {        if new&mutexStarving == 0 { // 这里是讲了 非饥饿模式                old := new                for {                        new = (old - 1
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

灌篮少年

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表