景象
func TestLock(t *testing.T) { l := &lockWrapper{} var wg sync.WaitGroup wg.Add(2) go func(){ s := 0 for i := 0 ; i < 0 ; 1000000; i++{ s += l.Get() } t.Log(s) wg.Done() }() go func() { for i := 0 ; i < 0 ; 1000000; i++{ l.Set(i) } t.Log(s) wg.Done() }() wg.Wait()}type lockWrapper struct { mu sync.RWMutex a int}func (l *lockWrapper) Get() int { l.mu.RLock() defer l.mu.RUnlock() //... l.mu.RLock() defer l.mu.RUnlock() return a}func (l *lockWrapper) Set(s int) { l.mu.Lock() defer l.mu.Unlock() l.a = s}
上述代码可能导致死锁
剖析
在Go中,先说论断Lock()
是优先于RLock()
的,如果在一个协程中重入同一个RLock
而另一个协程并行地调用Lock
,这种状况下就会造成死锁
seq | g0 | g1 |
---|---|---|
0 | RLock | |
1 | Lock | |
2 | RLock |
在执行Lock时
func (rw *RWMutex) Lock() { if race.Enabled { _ = rw.w.state race.Disable() } // 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) } if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(&rw.readerSem)) race.Acquire(unsafe.Pointer(&rw.writerSem)) }}
而执行RLock 则会因为Lock批改了标记位而陷入期待,造成死锁
func (rw *RWMutex) RLock() { if race.Enabled { _ = rw.w.state race.Disable() } if atomic.AddInt32(&rw.readerCount, 1) < 0 { // A writer is pending, wait for it. runtime_SemacquireMutex(&rw.readerSem, false, 0) } if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(&rw.readerSem)) }}