课程章节:6-5、6-6、6-7、6-8
课程讲师:Moody
课程内容:
※ 锁饥饿
sync.Mutex 锁,当协程获取锁一直获取不到,超过1ms,就会切换到饥饿模式。
锁饥饿模式:
新来的协程如果没有得到锁,不再自旋等待,而是直接进入休眠队列
休眠队列里面被唤醒的协程,不需要继续竞争锁,而是直接得到锁
当sema的休眠队列已经清空,那么锁将从饥饿模式中退出,恢复到正常模式
※ 使用锁要注意
尽量减少锁的使用时间,只锁关键点
善用defer来释放锁,防止业务panic导致的锁无法释放问题
---------------
※ 读写锁
读锁是共享锁,写锁是独享锁
当写锁没有上的时候,多个协程可以同时持有一把读锁。
读锁没有全部释放,写锁不能添加,只能进入等待队列
写锁同时只能有一个协程持有,后面希望持有该写锁的协程都会进入等待队列
写锁被锁定的时候,读锁不能上,防止出现脏读
w 互斥锁为写锁
writerSem 作为写协程队列
readerSem 作为读协程队列
readerCount 正值:持有读锁的读协程的个数 负值:加了写锁
readerWait 当写操作被阻塞时等待的读协程的个数
※ 写锁
加写锁:有读锁的情况
sync.RWMutex.Lock方法
func (rw *RWMutex) Lock() {
rw.w.Lock()
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
}
调用Lock方法会阻塞后续的写操作,因为互斥锁已经被获取,所以其他协程获取写锁的时候会进入自旋或者进入休眠队列
调用atomic.add减去一个rwmutexMaxReaders,这个rwmutexMaxReaders的值是1<<30,数字非常大。再加上rwmutexMaxReaders,得到原值r。这一步的操作主要是先加,加成功后才有后续操作。这一步主要是判断 readerCount是否为0,如果不为0,则说明仍然有读协程持有读锁。
如仍有其他的协程持有读锁,那么写协程就进入休眠等待,执行runtime_SemacquireMutex方法。
所有的读锁释放之后,才会放出writerSem信号量唤醒写的休眠队列。
解写锁:
sync.RWMutex.Unlock方法
func (rw *RWMutex) Unlock() {
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
}
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
rw.w.Unlock()
}
调用atomic.AddInt32方法把readerCount还原为正数,如果大于rwmutexMaxReaders,说明根本就是在读锁的状态,完全不需要释放写锁,弹出去。注意throw是底层runtime包才有的报错,和panic不一样,他会直接down掉整个进程,属于无法通过 defer 和 recover 捕获的崩溃,需要从系统的角度重新把进程拉起来。
通过for循环,释放掉所有因写锁导致进入等待队列的读协程,读锁是共享锁,所以可以一次释放掉所有的读锁。readerWait还原为0
rw.w.Unlock私房掉w的锁。
※ 读锁
加读锁:readerCount > 0 :这种情况非常简单,如果readerCount大于0,说明已经有其他协程获取了读锁,写锁肯定因为互斥进入了休眠队列,那么直接readerCount+1,读锁加上就行了。
如果readerCount < 0 :则说明当前已经加了写锁,就会进入到休眠队列,并给readerWait+1
解读锁:readerCount >= 0 : 说明当前还有其他协程拿了读锁,所以可以直接解除readerWait,readerCount都减去1,只有最后一个读协程解锁后,判断readerCount-1=0,那么就去看写队列有没有协程等待拿写锁,唤醒,拿写锁。
如果readerCount < 0:
有一个写锁正在执行,这时候会调用sync.RWMutex.rUnlockSlow方法
func (rw *RWMutex) rUnlockSlow(r int32) {
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
throw("sync: RUnlock of unlocked RWMutex")
}
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
此方法会减少获取锁的写操作等待的读操作数readerWait-1,意思是,我不去竞争这个锁了,我不排队了。
共同学习,写下你的评论
评论加载中...
作者其他优质文章