【问题标题】:How can I compute the message to be sent on a channel as late as possible?如何尽可能晚地计算要在通道上发送的消息?
【发布时间】: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


【解决方案1】:

This answer(代表Go non-blocking channel send, test-for-failure before attempting to send?)建议使用第二个通道将信号从消费者发送到生产者:

  1. 消费者想要收到一条消息(例如,在收到来自timer.Ticker 的勾选后)。
  2. 消费者通过侧通道向生产者 goroutine 发送信号。 (因此,对于这个侧通道,生产者/消费者角色是相反的)。
  3. 生产者接收来自侧通道的信号。
  4. 生产者开始计算真正的消息。
  5. 生产者通过主渠道发送消息。
  6. 消费者收到消息。

【讨论】:

  • 这对我来说似乎过于复杂了。事实上,整个场景似乎是不必要的复杂性,没有任何收获。
猜你喜欢
  • 1970-01-01
  • 2011-01-25
  • 1970-01-01
  • 2020-02-25
  • 1970-01-01
  • 2013-06-27
  • 2019-01-14
  • 2012-06-06
  • 1970-01-01
相关资源
最近更新 更多