11 | 通道的高级玩法
11 | 通道的高级玩法
讲述:黄洲君
时长13:54大小6.36M
赞 29
提建议
精选留言(58)
- 江山如画2018-09-05感觉方法应该挺多,就看解决的是不是优雅 第一个问题:发现某个channel被关闭后,为了防止再次进入这个分支,可以把这个channel重新赋值成为一个长度为0的非缓冲通道,这样这个case就一直被阻塞了: for { select { case _, ok := <-ch1: if !ok { ch1 = make(chan int) } case ..... : //// default: //// } } 第二个问题:可以用 break和标签配合使用,直接break出指定的循环体,或者goto语句直接跳转到指定标签执行 break配合标签: ch1 := make(chan int, 1) time.AfterFunc(time.Second, func() { close(ch1) }) loop: for { select { case _, ok := <-ch1: if !ok { break loop } fmt.Println("ch1") } } fmt.Println("END") goto配合标签: ch1 := make(chan int, 1) time.AfterFunc(time.Second, func() { close(ch1) }) for { select { case _, ok := <-ch1: if !ok { goto loop } fmt.Println("ch1") } } loop: fmt.Println("END")展开共 7 条评论100
- 任性😀2018-09-25demo24里边少了rand.Seed(time.Now().Unix()),不然每次随机数都是固定的顺序58
- 左氧佛沙星人2019-03-25老师好,demo25中的这段代码我没看懂,不是这个匹配上了吗?为啥没有执行呢?我理解应该打印The second candidate case is selected.。。。。 ``` case getChan(1) <- getNumber(1): ``` 能指点一下吗?展开
作者回复: 因为 make(chan int) 初始化的是不带缓冲的通道。非缓冲通道只有在收发双方都就绪的情况下才能传递元素值,否则就阻塞。
共 2 条评论31 - 到不了的塔2018-10-24郝老师,请问第一题的答案是啥,不知道怎么屏蔽分支呢
作者回复: 设置为nil就可以了。
共 2 条评论18 - 笨笨2018-09-05谢谢赫老师今日分享,回答问题如下 1.对于select中被close的channel判断其第二个boolean参数,如果是false则被关闭,那么赋值此channel为nil,那么每次到这个nil的channel就会阻塞,select会忽略阻塞的通道,如果再搭配上default就一定能保证不会被阻塞了。 2.通过定义标签,配合goto或者break能实现在同一个函数内任意跳转,故可以跳出多层嵌套的循环。展开
作者回复: 你的问题是什么?
17 - zhaopan2019-02-19老师好: 仅当select语句中的所有case表达式都被求值完毕后,它才会开始选择候选分支。 当接收通道操作有多个满足条件时, 这里的所有case表达式都被求值完毕, 应该怎么理解? 是多个case表达式都能接收到通道的数据么? 如果都接收了, 随机选择一个分支去处理接收的通道数据. 那其他满足条件的case分支怎么执行到了? 如果是外层加for循环, 重新select语句, 那上一次select的操作其他满足条件未被选择的case还能收到上一次的数据么? 这里的原理是什么呢?展开
作者回复: 这里只会检查一下接收操作或发送操作是否可以进行(是否不会被阻塞)。有兴趣的话可以看一下 runtime/select.go 中的 selectgo 函数的源码。
11 - 🐻2019-02-10https://stackoverflow.com/questions/25469682/break-out-of-select-loop for-select break 方法。9
- 阿海2019-01-05运行了demo25.go, 发现结果是No candidate case is selected,原因跟 var channels = [3]chan int{ nil, make(chan int, 1), nil, } 有关,因为channels[0], channels[2]都是nil,所以select case时阻塞,而channels[1]初始化为无缓存channel,当没有从channels[1]取值时,select case阻塞,所以一轮下来,没有符合的条件case,只能运行case default了。展开共 1 条评论8
- 啵啵2018-09-11第一个问题,如果判断到chan关闭,即取到的第二个值为false。则将该chan赋值为nil。 第二个问题,根据情况使用goto或者return。或者加一个是否结束的标识,goto然后用两个break。9
- heha372018-11-05当第二个boolean参数为false的时候,在相应的case中设置chan为nil零值,再次case求值的时候会遭遇阻塞,会屏蔽该case。
作者回复: 是的。
8 - Tron2019-09-04请教老师一个问题, 如果我用context 取消一个正在执行的下载任务,形如: select { case i := <- jobs: downloadBigBigFile() case ctx <-Done return } 当 父进程发出cancel 指令后, 能够取消downloadBigBigFile()里面运行的任务吗?展开
作者回复: 首先修正一点,这与进程无关,与 goroutine 有关,而且没有父子关系。 正题回答:不能。因为程序流程已经走到 downloadBigBigFile() 这里了,不可能在没有循环或跳转的情况下再往回走。 看起来你应该把 ctx 放入 downloadBigBigFile 函数,然后在这个函数里做判断。
6 - Flo2019-04-23对zhaopan的问题中老师的回复存在疑惑,老师回复如下:“作者回复: 这里只会检查一下接收操作或发送操作是否可以进行(是否不会被阻塞)。有兴趣的话可以看一下 runtime/select.go 中的 selectgo 函数的源码。” -----这里是不是表示,对于那些符合条件但没有执行到的case,之前判断的时候是不是并没有从chan中取出数据?
作者回复: 不好意思,问问题的人太多了,你问的时候最好带上比较完整的上下文。我下面按我目前对你问题的理解回答你吧。 select 语句对某个 case 中的通道接收表达式的实际执行需要两个前提条件: 1. 接收操作不会被阻塞。 2. 当前 case 被选中。 对于 case 中的通道发送表达式来说也是类似。因此,我们完全不用担心那些未被选中的通道操作会突然执行。
5 - 癫狂的小兵2018-09-05请问当select语句发现多个分支满足条件时随机选择一个分支执行 那怎样让其他满足条件的分支执行呢? for 循环 等待下一次循环时再执行?
作者回复: 放在for循环里每次也是随机的,不过可以用for循环,或者再次执行select语句。
5 - 长杰2019-01-13函数定义,有的用首字母大写的命名规则,有的用驼峰命名规则,老师能介绍一下go语言编程的规范介绍吗?
作者回复: 你可以看看我在前面的(程序实体相关的)文章中讲的。程序实体的名称如果首字母大写那么就说明是其访问权限是公开的,否则就是包级私有的。
4 - 许森森2018-09-141 发现channel是closed之后,重新make,使得为nil,保证一直 阻塞。 i := 0 for { select { case _, ok := <-c: if !ok { c = make(chan bool) fmt.Println("!ok") } else { fmt.Println("c:", c) } default: time.Sleep(1e9) // 等待1秒钟 fmt.Println("default, ", i) i = i + 1 if i > 3 { return } } } 2 return 直接退出程序 break loop // 结合loop,会退出for循环 goto end //结合end,跳转 //在select语句与for语句联用时,怎样直接退出外层的for语句? tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) //loop: for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") // return //直接退出程序 //break loop // 结合loop,会退出for循环 goto end //结合end default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } end: fmt.Println("END...")展开5
- Kennedy2018-09-10通道的类型如果是方法,性能会差吗?4
- 水先生2019-12-19“二、当intChan2中没有元素值时,它会被阻塞在有for关键字的那一行,直到有新的元素值可取。” 老师,我有一个疑问:for...range..会把channel的元素全部取出来的,意思是到最后,就会阻塞么?
作者回复: 在这里不会,因为getIntChan函数已经把intChan2关闭了。 文章里的描述我改进了一下: 2. 通常,当通道 intChan2 中没有元素值时,这条 for 语句会被阻塞在有 for 关键字的那一行,直到有新的元素值可取。不过,由于这里的 getIntChan 函数会事先将 intChan2 关闭,所以它在取出 intChan2 中的所有元素值之后会直接结束执行。 后面我会让编辑帮我更新一下。
3 - 一只傻哈皮2019-02-22请问select伪随机执行的目的是什么呢?没太理解这样做的目的。
作者回复: 这属于语言特性之一,没有什么特殊原因。你可以理解为避免case书写顺序影响到执行顺序。
共 2 条评论3 - Neo2018-09-12请问老师: select 分支选择规则中第5个:"如果select语句发现同时有多个候选分支满足选择条件,那么它就会用一种伪随机算法在这些分支中选择一个执行" 随机选一个执行 那我们是不是就不能确定程序会执行哪一条与语句了?
作者回复: 这种情况下是这样。
3 - Forever2018-09-10老师请问如果我是100个管道并行读入1亿的数字 然后100个管道分别去调用判断数字是否是质数的函数 然后本来运行着好好的cpu占用率到了99 后来突然不再运算了 cpu也直接占用率骤降到5% 但是程序没有报错而是暂停不动 请问老师是什么原因共 1 条评论2