景象

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,这种状况下就会造成死锁

seqg0g1
0RLock
1Lock
2RLock

在执行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))    }}