golang笔记 mutex,抢占式调度,semaphore

Mutex

正常模式 饥饿模式

在这里插入图片描述
在这里插入图片描述
一个goroutine获得锁的过程
1,g先自旋几次尝试获得锁,失败后进入等待队列排队等待

自旋 /等待队列

在这里插入图片描述

2 等待队列前面的g在排到的时候先要与自旋中的g竞争
3 而等待队列中的g大概率拿不到锁,它会被放到原来的位置(头部)继续等待
4 当这个g等待超过1ms后,会将mutex从正常模式切换到饥饿模式
5 mutex在饥饿模式下,不在执行自旋,所有的goroutine都要排队,先进先出
在这里插入图片描述
以上
1正常模式可以保证高并发,但是有可能出现尾端延迟
2饥饿模式则解决的尾端延迟的问题,但牺牲了一定的性能

lock和unlock

在这里插入图片描述

1第一位记录是否加锁
2第二位记录是否有g被唤醒
3mutex工作模式 0正常模式,1jie模式
4 其它位记录有多少个等待着
在这里插入图片描述
lock()和unlock()方法调用了atomic操作,对mutexwritershift位的数据原子的修改,来获得锁或取消锁
在这里插入图片描述
在这里插入图片描述

1自旋状态的g尝试修改state中的mutexwoken位,以告诉持有锁的g在unlock时已经有g被唤醒了,不要再去唤醒其他g了
2每个g自旋4次,每次自旋前都要判断自旋次数,有没有释放锁,或者进入饥饿模式,
3自旋结束后通过atomic操作state,成功则是抢到锁或唤醒标识位进入排队,失败则从头再来
3如果等待1ms还没有拿到锁,就将mutex设置成饥饿模式

抢占式调度

linux下查看goroutine
1 top命令查到pid
在这里插入图片描述
2 对于一个正在运行的程序,可以用dlv attach $pid来追踪

在这里插入图片描述
3 grs命令查看当前所有的协程
在这里插入图片描述

在这里插入图片描述
来分析下grs输出的欣信息:
1 goroutine1和goroutine6是我代码创建的,其余是runtime创建的,
2goroutine对应代码第12行,即mian.mian
3 goroutine6对应第9行,
4 goroutin前面的星号表示当前调试绑定1号协程

在这里插入图片描述

gr 6 切换到6号协程
在这里插入图片描述
再通过bt栈回溯
在这里插入图片描述
是STW导致阻塞,STW要抢占所有的p,使其让出cpu,让gc开始工作

在这里插入图片描述
1,gc记录当前需要等待多少个p

在这里插入图片描述

2 当前p,系统正在调用的p,以及空闲的p,将其设置成pgcstop即可,对于当前正在运行的G的P,将其stackguard设置成stackpreement,标识gc正在等待让出,gcwaiting=1
在这里插入图片描述
具体是如何抢占呢?
1,前面了解了函数在编译阶段编译器会在函数头部插入检测代码,检测函数是否需要栈增长,档期设置成stackpreempt时即表示不会栈增长,而是执行schedule,调度时先检测gcwaiting的标识,若=1,则让出让gc开始工作
在这里插入图片描述
之所以出现上述问题,是因为代码中for只是空循环,也就不会插入栈增长代码,当然不会去检测gcwaiting

发送sigpreempt信号

在这里插入图片描述
抢占实现:
1 函数preemptone实现抢占p,4个步骤
a将g.preempt设置为ture
b 将g.startguard0设置成stackpreempt
c 判断硬件支持
d 判断用户是否允许,以上判断通过则将p.preempt设置成true
2 实际的抢占工作交由preemptM函数完成
3 preempt函数通过调用runtime.signalM发送信号给特定的M
4 发送信号调用了系统调用的SYS_tgkill发送给目标线程
以上实现了抢占的前半部分工作–信号发送,后面的工作有接受到信号的m完成

sighandler处理信号

在这里插入图片描述

1 m接受到sigpreenpt信号,交给sighandler来处理信号
2 sighandler函数确认信号为preempt后调用dosigpreempt函数,它会判断是否对指定的G异步抢占,通过:
a 指定的G与其对应的p,m的preempt字段都为true
b 指定的g处在grunning状态
c 指定的g可以安全的挂起,并对她进行栈扫描,没有打断写屏障
d 有足够的空间注入异步抢占函数
e 当前没有runtime相关的锁,不会引起死锁
3以上确认了要抢占,并且抢占式安全的,调用pushcall注入异步抢占函数
4 异步抢占函数asuncpreempt函数先保存现场,调用runtime.asyncpreempt2函数最终去执行schedule

在这里插入图片描述

所以查看到在go1.14之后,既是for空循环,也调用了asyncpreempt2函数
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值