23

不要等离职了,才知道for select时,如果通道已经关闭会怎么样?

 3 years ago
source link: https://segmentfault.com/a/1190000025124694
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

uiaAbyq.png!mobile

golang面试官:for select时,如果通道已经关闭会怎么样?如果select中只有一个case呢?

问题

for 循环 select 时,如果通道已经关闭会怎么样?如果 select 中的 case 只有一个,又会怎么样?

怎么答

select

解释

1.for循环里被关闭的通道

JJNJ3y.png!mobile

N3EvIzm.png!mobile

  • c通道 是一个缓冲为 0 的通道,在 main 开始时,启动一个协程对 c通道 写入 10 ,然后就关闭掉这个通道。
  • main 中通过 x, ok := <-c 接受 通道c 里的值,从输出结果里看出,确实从通道里读出了之前塞入通道的 10 ,但是在通道关闭后,这个通道一直能读出内容。

2.怎么样才能不读关闭后通道

2uMRrm7.png!mobile

  • x, ok := <-c 返回的值里第一个x是通道内的值, ok 是指通道是否关闭,当通道被关闭后, ok 则返回 false ,因此可以根据这个进行操作。读一个已经关闭的通道为什么会出现false,可以看我之前的 对已经关闭的的chan进行读写,会怎么样?为什么?
  • 当返回的 okfalse 时,执行 c = nil 将通道置为 nil ,相当于读一个未初始化的通道,则会一直阻塞。至于为什么读一个未初始化的通道会出现阻塞,可以看我的另一篇 对未初始化的的chan进行读写,会怎么样?为什么?select 中如果任意某个通道有值可读时,它就会被执行,其他被忽略。则 select 会跳过这个阻塞 case ,可以解决不断读已关闭通道的问题。

3.如果select里只有一个已经关闭的case,会怎么样?

eeeE3yE.png!mobile

  • 可以看出只有一个 case 的情况下,则会 死循环
  • 那如果像上面一个 case 那样,把通道置为 nil 就能解决问题了吗?

4.select里只有一个已经关闭的case,置为nil,会怎么样?

2YrUBvm.png!mobile

  • 第一次读取 case 能读到通道里的 10
  • 第二次读取 case 能读到通道已经关闭的信息。此时将通道置为 nil
  • 第三次读取 case 时main协程会被阻塞,此时整个进程没有其他活动的协程了,进程 deadlock

总结

  • select 中如果任意某个通道有值可读时,它就会被执行,其他被忽略。
  • 如果没有 default 字句, select 将有可能阻塞,直到某个通道有值可以运行,所以 select 里最好有一个 default ,否则将有一直阻塞的风险。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK