共计 1301 个字符,预计需要花费 4 分钟才能阅读完成。
景象
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)) | |
} | |
} |
正文完