go踩坑合集
RWMutex RLock重入导致死锁
RWMutex,即读写锁,可以被多个的reader或一个writer获取使用。
死锁例子
在使用RWMutex的时候,同一个reader是不应该连续调用Rlock
多次的,这样做不但没有意义,还有可能导致死锁,具体代码如下:
1 | func main() { |
sync.RWMutex分析
下面RWMutex的实现,我们来看这段代码的具体执行。为了方便理解,把if race.Enabled {...}
的相关代码都去除了。
goroutine 1:
l.RLock()
1
2
3
4
5
6func (rw *RWMutex) RLock() {
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
}执行
l.RLock()
后,goroutine 1获得写锁。- 状态:获得读锁
- readerCount = 1,readerWait = 0
goroutine 2:
l.Lock()
1
2
3
4
5
6
7
8
9
10func (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_SemacquireMutex(&rw.writerSem, false, 0)
}
}由于goroutine 1已获得写锁,此时goroutine 2等待。
- 状态:等待reader释放读锁
- readerCount = 1 - rwmutexMaxReaders,readerWait = 1
goroutine 1:
l.RLock()
1
2
3
4
5
6func (rw *RWMutex) RLock() {
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
}goroutine 1发现readerCount为负,认为有writer获得了写锁,接着也进入了等待状态。
- 状态:等待
- readerCount = 2 - rwmutexMaxReaders,readerWait = 1
最后goroutine 1和goroutine 2都进入了等待状态。
总结
readerCount的作用? 持有读锁的reader数。置为负时,代表了writer正在或者已经获得了读锁,此时其他reader不能再获得写锁。
readerWait的作用,以及在
Lock()
中,为何需要同时判断r != 0
和atomic.AddInt32(&rw.readerWait, r) != 0
? 置readerCount为负的时候,获得了写锁,但尚未RULock的reader数。writer需要等待这些reader执行结束。- 若
r == 0
,则无正在持有读锁的reader,可以直接完成读锁的加锁。 - 若
r != 0
,writer需要等待获得了写锁,但尚未RULock的reader执行结束。如何判断是否需要等待呢,即readerWait不为0的时候。同时reader决定是否能唤醒writer,也需要等到readerWait为0的时候。
1
2
3
4
5
6
7
8
9func (rw *RWMutex) Lock() {
rw.w.Lock()
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// 此刻起,其他reader不再能够获得读锁。
// 此时,尚未释放写锁的reader数为readerWait个,等待他们结束才能完成读锁的加锁。
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false)
}
}- 若
sync.WaitGroup使用的问题
例子
在实现一个需求的时候,需要等待一定数目的go协程执行完毕,但这个数目事先并不好确定。想到了可以用sync.WaitGroup来完成,在使用时候发现,Wait()
没有生效,并未等待协程结束,代码大致如下,
1 | func main() { |
执行后程序不会有任何的输出就退出了。
sync.WaitGroup源码分析
Typically this means the calls to Add should execute before the statement creating the goroutine or other event to be waited for.
总结
1 | func main() { |
References
goroutine
1 | func process(ctx context.Context, wg *sync.WaitGroup, retCh chan int) { |