景象
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))
}
}