原文链接

channels允许 goroutine 通过使用信号语义相互通信。channels通过使用发送/接收数据或通过识别单个channel上的状态变化来实现此信号。不要把channel当作队列来构建程序,而是把重点放在简化所需编排的信号和语义上。

设计指南

语言机制

  • 使用channels编排和协调 goroutine
    • 关注信号语义而不是数据共享。
    • 有数据或无数据的信号
    • 质疑它们用于同步对共享状态的访问
      • 在有些情况下,channel可以更简单,但是最初的问题
  • 无缓冲的channels:
    • 接收在发送之前
    • 好处: 能100% 保证发送的信号已被接收
    • 成本: 接收信号时的延迟未知
  • 带缓冲的channels:
    • 发送在接收之前
    • 好处: 减少信号之间的阻塞延迟
    • 成本: 不能保证发送的信号何时被接收到
      • 缓冲区越大,保证性越差
      • 缓冲区为1可以给你一个延迟发送的保证
  • 关闭channels:
    • 关闭在接收之前。(如缓冲)
    • 没有数据的信号
    • 非常适合信号取消和最后期限
  • nil channel :
    • 发送和接收块
    • 关闭信号
    • 非常适合速度限制或短期停顿

设计理念:
解决不同的问题,可能需要不同的channel语义。根据您需要的语义,必须选择不同的体系结构。

  • 如果任何给定的发送给channel 会阻塞正在发送的goroutine:
    • 谨慎使用大于1的缓冲通道
      • 大于1的缓冲区必须有原因/测量值
    • 必须要明白发送goroutine 阻塞时会发生什么
  • If any given Send on a channel 如果任何给定的发送在一个频道WON’T cause the sending goroutine to block: 因为派 goroutine 去阻止:
    • 每次发送都保证有确切的缓冲区数量
      • 扇出模式
    • 确认你测试过缓冲的最大容量
      • 下降模式
  • 有了缓冲区,越小越好
    • 在考虑缓冲区时,不要考虑性能
    • 缓冲区可以帮助减少信令之间的阻塞延迟
      • 将阻塞延迟降低到零并不一定意味着更好的吞吐量
      • 如果一个缓冲区能够提供足够的吞吐量,那么就保留它
      • 对大于1的缓冲区提出质疑,并对其大小进行度量
      • 寻找尽可能小的缓冲区,以提供足够好的吞吐量

        图解

        传递保证

        传递保证是基于一个问题:“我是否需要保证特定goroutine发送的信号已收到?”
        image.png

        有数据和没数据的信号

        当使用数据发出信号时,可以选择三个channel配置选项,具体取决于所需的保证类型。
        image.png
        无数据信号主要用来取消。它允许一个goroutine用信号通知另一个goroutine来取消他们正在做的事情并继续执行。可以使用无缓冲和缓冲的channel来实现取消。
        image.png

        状态

        channel的行为直接受其当前状态的影响,其只有三个状态:nil,open,close
        image.png

参考链接:

  1. The Behavior Of Channels — William Kennedy
  2. Channel Communication
  3. Share Memory By Communicating — Andrew Gerrand
  4. The Nature Of Channels In Go— William Kennedy
  5. Understanding Channels - Kavya Joshi

缓冲扩容 - 2011

  • 大缓冲能及时阻止大量警告。
  • 它们可以在短时间内增强处理洪峰的能力。
  • 它们可以加快响应速度而不是减少。
  • 使用buffered channels 可以提供一种保持连续性的方法

代码评审

Basic mechanics (Go Playground)
Tennis game (Go Playground)
Relay race (Go Playground)
Fan out pattern (Go Playground)
Monitor running time (Go Playground)

高级代码评审

Channel communication ordering (Go Playground)

练习

练习1:编写一个程序,其中两个 goroutine 来回传递一个整数十次。 当每个 goroutine 接收到整数时显示。 每次通过都增加整数。 一旦整数等于 10,干净地终止程序。
练习2:编写一个程序,使用扇出模式同时生成 100 个随机数。 让每个 goroutine 生成一个随机数,并通过缓冲通道将该数字返回给主 goroutine。 设置缓冲区通道的大小,以便永远不会发送阻塞。 不要分配比您需要的更多的缓冲区。 让主 goroutine 显示它收到的每个随机数,然后终止程序。
练习3:编写一个程序,最多同时生成 100 个随机数。 不要发送所有 100 个值,因为发送/接收的数量是未知的。
练习4:编写一个程序,使用工作池同时生成最多 100 个随机数。 拒绝偶数值。 已收集到 100 个奇数,就让协程停止运行。