在频道上进行非阻塞多重接收

时间:2014-10-07 02:17:51

标签: select go nonblocking

到处似乎都在讨论从频道阅读应始终是一种阻止操作。态度似乎是这就是Go的方式。这是有道理的,但我试图弄清楚如何从频道汇总事物。

例如,发送http请求。假设我有一个生成数据流的管道设置,所以我有一个生成点队列/流的通道。然后我可以使用goroutine监听此频道并发送HTTP请求以将其存储在服务中。这有效,但我为每个点创建了一个http请求。

我发送的端点也允许我批量发送多个数据点。我想做的是

  1. 读取尽可能多的值,直到我阻止频道。
  2. 合并/发送单个http请求。
  3. 然后在通道上阻止,直到我可以阅读 再一次。
  4. 这就是我使用线程安全队列和select语句在C中做的事情。基本上在可能的情况下刷新整个/队列缓冲区。这是一种有效的技术吗?

    似乎go select语句确实给了我一些与C选择相似的东西,但是我仍然不确定是否存在非阻塞读取'在频道上。

    编辑:我也愿意接受我打算可能不是Go Way的内容,但是不断打击非停止的http请求对我来说似乎也是错误的,特别是如果它们可以聚合的话。如果有人有一个很酷的替代架构,但我想避免像神奇地缓冲N个项目,或等待X秒直到发送。

2 个答案:

答案 0 :(得分:3)

以下是在通道为空之前批处理的方法。变量batch是数据点类型的一部分。变量ch是数据点类型的通道。

var batch []someType
for {
    select {
    case v := <-ch:
       batch = append(batch, v)
    default:
       if len(batch) > 0 {
           sendBatch(batch)
           batch := batch[:0]
       }
       batch = append(batch, <-ch)  // receiving a value here prevents busy waiting.
    }
}

您应该阻止批次无限制地增长。这是一个简单的方法:

var batch []someType
for {
    select {
    case v := <-ch:
       batch = append(batch, v)
       if len(batch) >= batchLimit {
           sendBatch(batch)
           batch := batch[:0]
       }
    default:
       if len(batch) > 0 {
           sendBatch(batch)
           batch := batch[:0]
       }
       batch = append(batch, <-ch)
    }
}

答案 1 :(得分:2)

Dewy Broto为您的问题提供了很好的解决方案。这是一个直接的直接解决方案,但我想更广泛地评论如何为不同问题寻找解决方案。

Go使用Communicating Sequential Process代数(CSP)作为渠道,选择和轻量级流程的基础(&#39; goroutines&#39;)。 CSP保证事件的顺序;当你做出选择时,它只会引入非决定论(a.k.a。select)。保证订购有时被称为&#34;发生之前&#34; - 它使编码比替代(广泛流行)非阻塞风格简单得多。它还为创建组件提供了更多的空间:长寿命功能单元,通过可预测的方式通过通道与外部世界互动。

或许在频道上谈论阻止会给人们学习Go带来心理障碍。我们在I / O上阻止,但我们在渠道上 等待 。只要系统整体上有足够的并行松弛(即其他活动的goroutine)来保持CPU忙碌,在通道上等待就不要皱眉了。

可视化组件

所以,回到你的问题。让我们根据组件来考虑它,您需要探索许多要点。假设每个源都是goroutine,它然后在输出通道中形成一个组件。 Go允许共享通道末端,因此许多源可以安全地将它们的点交错到单个通道上。你不必做任何事情 - 它只是渠道的运作方式。

Dewy Broto描述的批处理功能本质上是另一个组成部分。作为一种学习练习,以这种方式表达是一件好事。批处理组件有一个点输入通道和一个批处理输出通道。

最后,HTTP i / o行为也可以是具有一个输入通道且没有输出通道的组件,仅用于接收整批点,然后通过HTTP发送它们。

仅使用一个来源的简单情况,可能会这样描述:

+--------+     point     +---------+     batch     +-------------+
| source +------->-------+ batcher +------->-------+ http output |
+--------+               +---------+               +-------------+

这里的目的是描述在基础层面的不同活动。它有点像数字电路图,并不是巧合。

你确实可以在Go中实现它,它会起作用。它甚至可能运行得很好,但实际上您可能希望通过组合成对组件来优化它,并根据需要重复进行。在这种情况下,很容易将batcher和http输出结合起来,最终得到Dewy Broto的解决方案。

重要的是,Go并发最容易发生

  • (a)不要担心阻塞;
  • (b)描述了需要在相当细粒度的水平上进行的活动(在简单的情况下,你可以在脑海中做到这一点);
  • (c)如有必要,可以将功能组合在一起进行优化。

我将挑战移动频道可视化的更高级主题(Pi-Calculus)作为挑战,其中使用频道将频道结束发送到其他goroutines。