「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」。
go语言学习的过程中经常会遇到select,感觉并不是很懂,导致学习过程中有些寸步难行。因此在本篇文章中专门学习下。
先举个例子
func findMinStep(board string, hand string) int {
chan1 := make(chan int)
chan2 := make(chan int)
go func(){
chan1 <- 1
time.Sleep(time.Second)
}
go func(){
chan2 <- 2
time.Sleep(time.Second)
}
select {
case <- chan1:
fmt.Println("case1 !")
case <- chan2:
fmt.Println("case2 !")
default:
fmt.Println("default !")
}
}
elect语句分别检测chan1和chan2是否可读,如果都不可读则执行default语句。
那么如果没有default呢
select如果没有default,就会去查看case里通道是否有值,如果通道没有值,就会造成阻塞。直到case里的通道有值为止。
那么为什么default如此特殊呢?或者说在select的时候程序究竟在做什么呢?
case语句
我们查看runtime包下面的select.go函数,发现了scase的结构体
type scase struct {
c *hchan // chan
elem unsafe.Pointer // data element
}
看起来c就代表是一个通道,也是对应了我们每个case都会有一个通道的特点。这个指针没发现是干啥用的,暂时留下。
接着往下看
简单介绍下两个加锁解锁的函数
func sellock(scases []scase, lockorder []uint16) {
var c *hchan
for _, o := range lockorder {
c0 := scases[o].c
if c0 != c {
c = c0
lock(&c.lock)
}
}
}
这个加锁代码简单,也很有意思。从参数上看是简单的scase数组和lockorder数组,通过for循环我们也可以理解为代码需要根据lockorder的值对scases数组中的部分元素进行加锁,但是
if c0 != c {
c = c0
lock(&c.lock)
}
这份代码就比较奇怪,因为这代表着如果上次遍历的元素和这次加锁的对象相同,我们就不会重复加锁。
如果不想重复加锁,我们其实可以有很多种方式,而且一般来说这种也只是能在满足“如果上次遍历的元素和这次加锁的对象相同”条件下时,才不会重复加锁,看起来有点鸡肋,不过在假设代码没问题(事实上也是没问题)的情况下,我们可以大胆反推:lockorder是一个有序的数组。