DevilKing's blog

冷灯看剑,剑上几分功名?炉香无需计苍生,纵一穿烟逝,万丈云埋,孤阳还照古陵

0%

Go Lock

原文链接

golang部分的锁,主要在sync包里

runtime/sema(信号量)

runtime中的sema.go,信号量的同步功能是作用于goroutine的

1
2
3
4
5
6
7
8
9
10
type semaRoot struct {
lock mutex
head *sudog
tail *sudog
nwait uint32 // Number of waiters. Read w/o the lock.
}
// 主要结构就是一个sudog的链表和一个nwait。
// 链表用于存储等待的goroutine,nwait表示在该信号量上等待的goroutine数目。
// lock一个互斥量,是在多线程环境中保护链表的。(但是这个mutex不是sync中的mutex,是sema.go
// 内部使用的一个私有版本)

golang设置了可操作信号量个数的最大量是251,相关操作,还是看链表的操作

sync/atomic

这里原子操作,是保证多个cpu(协程)对同一块内存区域的操作是原子的

1
2
3
4
5
6
7
8
9
10
// 互斥量加锁操作
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
......

sync/mutex(互斥锁)

sync/mutex.go

1
2
3
4
5
6
7
8
9
10
11
12
13
// A Mutex must not be copied after first use.
type Mutex struct {
state int32 // 一个int32整数,用其中的位来表示
sema uint32
}

//iota,特殊常量,可以认为是一个可以被编译器修改的常量。
//在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
const (
mutexLocked = 1 << iota //值为1,表示在state中由低向高第1位,意义:锁是否可用,0可用,1不可用
mutexWoken // 值为2,表示在state中由低向高第2位,意义:mutex是否被唤醒
mutexWaiterShift = iota //值为2,表示state中统计阻塞在此mutex上goroutine的数目需要位移的偏移量
)

lock流程

简单来说,如果当前goroutine可以加锁,那么调用原子操作使得mutex中的flag设置成已占用达到互斥;如果当前goroutine发现锁已被占用,那么会有条件的循环尝试获取锁,这里是不用信号量去对goroutine进行sleep和wake操作的(尽可能避免开销),如果循环尝试失败,则最后调用原子操作争抢一次,获取不到则还是得调用runtime_Semacquire去判断阻塞goroutine还是继续争用锁。

sync/RWMutex(读写锁)

1
2
3
4
5
6
7
type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}

写锁定

1
2
3
4
5
6
7
8
9
10
func (rw *RWMutex) Lock() {
// First, resolve competition with other writers.
rw.w.Lock()
// Announce to readers there is a pending writer.
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_Semacquire(&rw.writerSem)
}
}