【发布时间】:2019-10-26 19:39:56
【问题描述】:
我的场景:
- 我有一个生产者和一个消费者。两者都是 goroutine,它们通过一个通道进行通信。
- 生产者能够(理论上)随时生成消息。
- 生成消息需要一些计算。
- 消息在一定程度上具有时间敏感性(即,消息越旧,相关性越低)。
- 消费者偶尔会从频道读取数据。在本示例中,假设消费者使用
time.Ticker每隔几秒读取一次消息。 - 消费者更喜欢“新鲜”的消息(即尽可能最近生成的消息)。
所以,问题是:生产者如何尽可能晚地生成消息?
显示总体思路的示例代码:
func producer() {
for {
select {
...
case pipe <- generateMsg():
// I'd like to call generateMsg as late as possible,
// i.e. calculate the timestamp when I know
// that writing to the channel will not block.
}
}
}
func consumer() {
for {
select {
...
case <-timeTicker.C:
// Reading from the consumer.
msg <- pipe
...
}
}
}
完整代码(与上面略有不同)可在 Go Playground 获得:https://play.golang.org/p/y0oCf39AV6P
我的一个想法是检查写入频道是否会阻塞。如果它不会阻塞,那么我可以生成一条消息然后发送它。不过……
- 我找不到任何方法来测试写入通道是否会阻塞。
- 在一般情况下,这是一个坏主意,因为如果我们有多个生产者,它会引入竞争条件。在这种特定情况下,我只有一个制作人。
另一个(坏)主意:
func producer() {
var msg Message
for {
// This is BAD. DON'T DO THIS!
select {
case pipe <- msg:
// It may send the same message multiple times.
default:
msg = generateMsg()
// It causes a busy-wait loop, high CPU usage
// because it re-generates the message all the time.
}
}
}
【问题讨论】:
-
如果您关心紧迫性,为什么要使用自动收报机?为什么不在消息准备好后立即阅读?
-
IMO,使用通道和 go-routine 来满足这个要求会达到目的。这是一个按需生产者消费者问题。如果您不想异步生成消息,即生产者不应该在消费者需要之前生成消息,那么您为什么首先需要通道和 go-routine。为什么不能直接调用该函数,如果 generateMsg 涉及访问关键内存,则使用 Mutex 保护它。 Mutex 并不邪恶,毕竟通道只是包裹 Mutex,据我所知,它隐藏了所有肮脏的细节。
-
我在场景中省略了
select语句的其他一些情况。生产者实际上是从其他渠道读取数据,计算一些即时统计数据,并将这些统计数据提供给消费者。消费者每秒只需要显示一次统计信息,因此,它每秒只需要消费一次。但也许像你说的那样太复杂了。 -
@DenilsonSáMaia:让它每秒计算一次统计数据。不需要消费者/生产者模式、goroutine 或通道。
-
同意。这似乎是共享内存的情况,“生产者”按计划更新,“消费者”按计划读取,并使用互斥锁以避免竞争。
标签: go nonblocking channel