现象
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)
}
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.
// 此处由于有writer会陷入等待,重入失败并保持读锁
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。