go:select channel源码

本文围绕Go语言展开,介绍了buffered和unbuffered channel的读写特点,Go Scheduler的工作窃取算法,hchan结构。阐述了向buffered channel发送数据的流程,重点讲解了select的执行步骤,包括随机执行case、按channel地址排序加锁防死锁,还说明了加锁解锁顺序的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考

go夜读
select源码
参考文章

buffered和unbuffered

buffered:

ch <- v happens before v <- ch

有buffer的情况下:先把值写到channel,才能读到

make(chan interface{}, size) 

unbuffered:

v <- ch happens before ch <- v

无buffer呢?先读了,之后才写。(没有中间人拿这个东西)

make(chan interface{}, 0)

Go Scheduler

每个M P 会有一些本地的go routine队列,先处理自己的(不用去用锁抢全局的G队列)
工作窃取算法:如果自己的搞完了,那就去全局/别人家偷一点玩。
在这里插入图片描述

hchan结构

是个环形队列
recvq、sendq:等待的队列

sudog: 包装go routine的指针
双向队列 prev next
elem: 数据元素
c: 当前阻塞在哪个channel

在这里插入图片描述
如果是unbuffered的,不分配ring buffer

向buffered channel发送

加锁然后放进ring buffer里就完事了
在这里插入图片描述
如果队列已满,还要发,那只能在发送队列sendq等待:
在这里插入图片描述

如果来了新的接收方:
在这里插入图片描述
先出队;然后把等待的在sendq要写的东西塞进ringbuffer;goroutine又可以回去等待调度了。

在这里插入图片描述
入队等待,park

在这里插入图片描述
如果接收的时候buf为空,则直接拿走,不经过ringbuffer,跟unbuffered的情况一模一样。

close:关闭
在这里插入图片描述
关闭还可以收,但是不能发了。

select怎么搞

在这里插入图片描述
在这里插入图片描述

先理顺执行步骤:

首先这种等待唤醒的肯定是一个循环(loop)
然后会需要随机地执行case(随机打乱数组然后按照随机顺序执行)
找到有io的case就执行相应的操作(随机打乱后第一个有io的被执行然后返回)

上面就是主要的步骤了。但是有个问题。这段代码会被并行执行。肯定需要对select中相关的channel进行加锁。如果单单是按照随机case去执行加锁,很可能发生死锁。

比如?协程1加锁顺序 1、2, 协程2加锁顺序 2、1;
协程1加锁了1,还没加锁2;换到协程2执行,加锁2,然后想加锁1失败,切回协程1,想加锁2失败,gg,谁也锁不上。

如果按照channel地址排序后再顺序加锁则不会发生死锁,每个协程加锁顺序都是1、2、3,搞不出死锁的情况。

具体代码:

这里会先生成两个排序,一个是pollorder:选择case的顺序(随机生成);一个是lockorder:锁定的顺序(防止死锁)。
具体是干嘛的?

pollorder好理解:就是随机生成轮询顺序。
lockorder是用来指定锁定顺序的。这个不太好理解。具体是怎么做的呢?

在这里插入图片描述

在这里插入图片描述
实际上就是按顺序把channel lock上了(注意可能相邻的是相同的channel)

unlock则是按照相反的顺序unlock
在这里插入图片描述
为什么需要反向解锁?感觉挺符合直觉的,但是究竟是为啥?假设不这样。

协程1: 加锁 1、2,解锁 1;
协程2: 加锁 1、2;
协程1: 解锁2。
协程2:??我明明加锁了 WTF?

但如果反向解锁:

协程1:加锁1、2,解锁2;
协程2:加锁1失败,让出
协程1:解锁1
协程2:加锁1、2、解锁2、1。

学到了啥?

当我们需要对一堆东西进行加锁解锁的时候,必须大家都按照一样的顺序加锁,再大家都按照相反的顺序解锁。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值