简体   繁体   English

进入频道非阻塞多个接收

[英]Go Nonblocking multiple receive on channel

Everywhere seems to discuss that reading from a channel should always be a blocking operation. 各地似乎都在讨论从通道读取应该始终是阻塞操作。 The attitude seems to be this is the Go way. 态度似乎就是这是Go的方式。 This makes some sense but I'm trying to figure out how I would aggregate things from channels. 这是有道理的,但是我试图弄清楚如何从渠道中汇总信息。

For example, sending http requests. 例如,发送http请求。 Say I have a pipeline setup that generates streams of data, so I have a channel that produces queue/stream of points. 假设我有一个生成数据流的管道设置,所以我有一个生成点队列/点流的通道。 I could then have a goroutine listen to this channel and send a HTTP Request to store it in a service. 然后,我可以让goroutine监听此频道并发送HTTP请求以将其存储在服务中。 This works, but I'm creating a http request for every point. 这可行,但是我正在为每个点创建一个http请求。

The endpoint I'm sending it too allows me to send multiple data points in a batch. 我发送的端点也允许我批量发送多个数据点。 What I would like to do, is 我想做的是

  1. Read as many values until I would block on channel. 读取尽可能多的值,直到我阻塞频道。
  2. Combine them/send single http request. 合并它们/发送单个http请求。
  3. Then block on channel until I can read one again. 然后在频道上阻止,直到我可以再读一次。

This is how I would've done things in C, with threadsafe queues and select statements. 这就是我使用线程安全队列和select语句在C中完成工作的方式。 Basically flushing the entire/queue buffer when possible. 基本上在可能时刷新整个/队列缓冲区。 Is this a valid technique in go? 这是行之有效的技术吗?

It seems the go select statement does give me something similar to C's select, but I'm still not sure if there is a 'nonblocking read' on channels. 似乎go select语句确实为我提供了类似于C的select的功能,但是我仍然不确定通道上是否有“非阻塞读取”。

EDIT: I'm also willing to accept what I'm intending may not be the Go Way, but constantly smashing off non stop http requests also seems wrong to me, especially if they can be aggregated. 编辑:我也很愿意接受我的意图,可能不是Go Way,但是不断粉碎不间断的HTTP请求对我来说似乎也是错误的,尤其是如果可以将它们汇总的话。 If someone has an alternative architecture that will be cool, but I want to avoid things like, magically buffering N items, or waiting X seconds until sending. 如果某人有一个很棒的替代体系结构,但是我想避免诸如神奇地缓冲N个项目或等待X秒直到发送这样的事情。

Here's how to batch until the channel is empty. 这是批量处理直到通道为空的方法。 The variable batch is a slice of your data point type. 可变batch是数据点类型的一部分。 The variable ch is a channel of your data point type. 变量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.
    }
}

You should prevent the batch from growing without limit. 您应防止批次无限制地增长。 Here's a simple way to do it: 这是一种简单的方法:

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)
    }
}

Dewy Broto has given a good solution to your problem. Dewy Broto为您的问题提供了很好的解决方案。 This is a straightforward direct solution, but I wanted to comment more broadly on how you might go about finding solutions for different problems. 这是一个直接的直接解决方案,但我想更广泛地评论您可能如何找到针对不同问题的解决方案。

Go uses Communicating Sequential Process algebra (CSP) as its basis for channels, selection and lightweight processes ('goroutines'). Go使用通信顺序过程代数(CSP)作为通道,选择和轻量级过程(“ goroutines”)的基础。 CSP guarantees the order of events; CSP保证事件的顺序; it only introduces non-determinism when you make it do so by making a choice (aka select ). 它仅在通过选择(又称为select )引入非确定性时引入。 The guaranteed ordering is sometimes called "happens-before" - it makes coding so much simpler than the alternative (widely popular) non-blocking style. 保证排序有时被称为“先于发生”-它使编码比替代(广泛流行)的非阻塞样式更加简单。 It also gives more scope for creating components: units of long-lived functionality that interact with the outside world through channels in a predictable way. 它还为创建组件提供了更大的空间:使用寿命长的功能单元,它们以可预测的方式通过渠道与外界交互。

Perhaps talk of blocking on channels puts a mental hurdle in the way of people learning Go. 谈论阻塞频道的话题可能会给人们学习Go带来障碍。 We block on I/O, but we wait on channels. 我们阻止 I / O,但我们等待通道。 Waiting on channels is not to be frowned on, provided that the system as a whole has enough parallel slackness (ie other active goroutines) to get on keeping the CPU busy. 只要整个系统具有足够的并行松弛度(即其他活动的goroutine)以继续保持CPU繁忙,就不必担心等待通道。

Visualising Components 可视化组件

So, back to your problem. 因此,回到您的问题。 Let's think about it in terms of components , you have many sources of points you need to explore. 让我们从组件的角度来考虑它,您有许多需要探索的观点来源。 Suppose each source is a goroutine, it then forms a component in your design with an output channel. 假设每个源都是一个goroutine,然后它在您的设计中通过一个输出通道构成一个组件。 Go lets channel-ends be shared, therefore many sources can safely interleave, in order, their points onto a single channel. Go允许共享通道端,因此许多源可以安全地按顺序将它们的点交错到一个通道中。 You don't have to do anything - it's just how channels work. 您无需执行任何操作-这就是渠道的工作方式。

The batching function described by Dewy Broto is, at essence, another component. Dewy Broto描述的批处理功能实质上是另一个组件。 As a learning exercise, it's a good thing to express it this way. 作为一种学习练习,用这种方式表达它是一件好事。 The batching component has one input channel of points and one output channel of batches. 批处理组件具有一个点的输入通道和一个批处理的输出通道。

Finally the HTTP i/o behaviour could also be a component with one input channel and no output channels, serving merely to receive whole batches of points then send them via HTTP. 最终,HTTP I / O行为也可以是具有一个输入通道而没有输出通道的组件,仅用于接收全部批次的点,然后通过HTTP发送它们。

Taking the simple case of only one source, this might be depicted like this: 以仅一个来源的简单情况为例,可能如下所示:

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

The intention here is to depict the different activities at their fundamental level . 这里的目的是从根本上描述不同的活动。 It's a bit like a digital circuit diagram and that's not a coincidence. 这有点像数字电路图,这不是巧合。

You could indeed implement this in Go and it would work. 您确实可以在Go中实现此功能,它将起作用。 It might even work well enough, but you may in practice prefer to optimise it by combining pairs of components, repeatedly as necessary. 它甚至可能工作得很好,但实际上您可能更愿意通过组合成对的组件来对其进行优化,并在必要时进行重复。 In this case, it's easy to combine the batcher and http output and doing so ends up with Dewy Broto's solution. 在这种情况下,可以很容易地将批处理程序和http输出结合起来,最终得到Dewy Broto的解决方案。

The important point is that Go concurrency happens easiest by 重要的一点是Go并发最容易发生

  • (a) do not worry up front about blocking; (a)不用担心担心阻塞;
  • (b) depict the activities that need to happen at a fairly fine-grained level (you can do this in your head in simple cases); (b)描述需要在相当细粒度的水平上进行的活动(在简单的情况下,您可以脑中做到);
  • (c) if necessary, optimise by combining functions together. (c)如有必要,将功能组合在一起进行优化。

I'll leave as a challenge the more advanced topic of visualising mobile channel ends (Pi-Calculus) where channels are used to send channel-ends to other goroutines. 作为挑战,我将剩下一个更高级的主题,即可视化移动通道末端(Pi-Calculus),其中通道用于将通道末端发送到其他goroutine。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM