读写锁重入导致死锁

参考:https://www.cnblogs.com/thinkeridea/p/10177781.html

总结下,就是golang的读写锁是写优先的,如果同时有读锁和写锁去竞争,会优先给写锁;但是如果有已经获取到读锁的,写锁也会等已经获取的读锁执行完毕后再去竞争。文章里面举得例发生的场景就是,正在进行的读锁->竞争的写锁->竞争的读锁,最终导致的问题就是读写锁互相等待,导致死锁。

复现问题:

package main

import (
	"fmt"
	"runtime"
	"sync"
)

var l = sync.RWMutex{}

func main() {
	var wg sync.WaitGroup
	wg.Add(2)
	c := make(chan int)
	go func() {
		l.RLock() // 读锁1
		defer l.RUnlock()
		fmt.Println(1)
		c <- 1
		fmt.Println(2)
		runtime.Gosched()
		fmt.Println(3)
		b()
		fmt.Println(4)
		wg.Done()
	}()

	go func() {
		fmt.Println(5)
		<-c
		fmt.Println(6)
		l.Lock()
		fmt.Println(7)
		fmt.Println(8)
		defer l.Unlock()
		fmt.Println(9)
		wg.Done()
	}()

	go func() {
		i := 1
		for {
			i++
		}
	}()
	wg.Wait()
}

func b() {
	fmt.Println(10)
	l.RLock() // 读锁2
	fmt.Println(11)
	defer l.RUnlock()
	fmt.Println(12)
}

执行的结果

分析:

  • 首先加上读锁1,就是 fmt.Println(1) 之前, 状态加读锁1
  • 另外一个 goroutine 启动,fmt.Println(5), 状态加读锁1
  • 发送数据 c <- 1 , 状态加读锁1
  • 接受到数据 <-c fmt.Println(6), 状态加读锁1
  • 输出 2 fmt.Println(2), 状态加读锁1
  • 暂停当前 goroutine runtime.Gosched() , 状态加读锁1
  • 申请写锁 l.Lock(), 等待读锁1释放, 状态加读锁1、写锁等待
  • 切换 goroutine 执行 fmt.Println(3) 与 b(), 状态加读锁1、写锁等待
  • 输出10 fmt.Println(10), 申请读锁2,等待写锁释放, 状态加读锁1、写锁等待、读锁2等待
  • 支持程序永久阻塞……

 

Tip:

  • 运行时离开当前逻辑就释放锁。
  • 锁的粒度越小越好,加锁后尽快释放锁。
  • 尽量不用 defer 释放锁。
  • 读锁不要嵌套。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值