问题
for循环select时,如果通道已经关闭会怎么样?如果select中的case只有一个,又会怎么样?
怎么答
- for 循环select时,如果其中一个 case 通道已经关闭,则每次都会执行到这个 case。
- 如果 select 里边只有一个 case,而这个 case 被关闭了,则会出现死循环。
解释
1.for 循环里被关闭的通道
- c通道是一个缓冲为- 0的通道,在- main开始时,启动一个协程对- c通道写入- 10,然后就关闭掉这个通道。
- 在main中通过x, ok := <-c接受通道c里的值,从输出结果里看出,确实从通道里读出了之前塞入通道的10,但是在通道关闭后,这个通道一直能读出内容。
2.怎么样才能不读关闭后通道
- x, ok := <-c返回的值里第一个 x 是通道内的值,- ok是指通道是否关闭,当通道被关闭后,- ok则返回- false,因此可以根据这个进行操作。读一个已经关闭的通道为什么会出现 false,可以看我之前的 对已经关闭的的 chan 进行读写,会怎么样?为什么? 。
- 当返回的ok为false时,执行c = nil将通道置为nil,相当于读一个未初始化的通道,则会一直阻塞。至于为什么读一个未初始化的通道会出现阻塞,可以看我的另一篇 对未初始化的的 chan 进行读写,会怎么样?为什么? 。select中如果任意某个通道有值可读时,它就会被执行,其他被忽略。则select会跳过这个阻塞case,可以解决不断读已关闭通道的问题。
3.如果 select 里只有一个已经关闭的 case,会怎么样?
- 可以看出只有一个case的情况下,则会死循环。
- 那如果像上面一个case那样,把通道置为nil就能解决问题了吗?
4.select 里只有一个已经关闭的 case,置为 nil,会怎么样?
- 第一次读取case能读到通道里的10
- 第二次读取case能读到通道已经关闭的信息。此时将通道置为nil
- 第三次读取case时 main 协程会被阻塞,此时整个进程没有其他活动的协程了,进程deadlock
总结
- select中如果任意某个通道有值可读时,它就会被执行,其他被忽略。
- 如果没有default字句,select将有可能阻塞,直到某个通道有值可以运行,所以select里最好有一个default,否则将有一直阻塞的风险。
文章推荐:
- golang 面试题:对已经关闭的的 chan 进行读写,会怎么样?为什么?
- golang 面试题:对未初始化的的 chan 进行读写,会怎么样?为什么?
- golang 面试题:reflect(反射包)如何获取字段 tag?为什么 json 包不能导出私有变量的 tag?
- golang 面试题:json 包变量不加 tag 会怎么样?
- golang 面试题:怎么避免内存逃逸?
- golang 面试题:简单聊聊内存逃逸?
- golang 面试题:字符串转成 byte 数组,会发生内存拷贝吗?
- golang 面试题:翻转含有中文、数字、英文字母的字符串
- golang 面试题:拷贝大切片一定比小切片代价大吗?
- golang 面试题:能说说 uintptr 和 unsafe.Pointer 的区别吗?