处理超时和收听频道

Handling timeouts and listening to channels

我的代码看起来像这样,我在其中收听频道直到超时间隔。假设这个 goroutine 1

select {
    case <-time.After(TimeoutInterval):
        mu.Lock()
        defer mu.Unlock()
        delete(msgChMap, index)
        return ""
    case msg := <-msgCh:
        return msg
}

在其他地方,我有一个 goroutine 2 运行这样的东西,它从 Map 中获取适当的 msgCh,删除 map 中的条目,然后通过通道发送消息。

mu.Lock()
msgCh, ok := msgChMap[index]
delete(msgChMap, index)
mu.Unlock()
if ok {
    msgCh <- "yay"
}

我好像可以从Map中抓取消息通道msgCh,尝试发送消息,但是因为TimeoutInterval已经过去了,所以没有任何监听通道,我的代码将卡住等待一个监听器。如果我在将 yay 发送到 msgCh 之后锁定,似乎我可能会死锁,因为 2 将等待通道的侦听器并且不会释放锁定,但是1 不再监听但需要锁定。

避免陷入等待侦听器的一般模式是什么?也许 go 足够聪明,不会卡在这里。

这里的问题是频道 reader 我在作者不知情的情况下停止了。应该可以构建此解决方案,使这种情况永远不会发生,但暂时忽略它,对于这个特定问题,您需要的是对通道本身的原子访问,以及通道状态的标志:

type channel struct {
   sync.Mutex

   msgCh chan Msg
   active bool
}

写入频道现在通过锁定完成:

ch.Lock()
if ch.active {
   ch.msgCh<-data
}
ch.Unlock()

当您“停用”频道时,重置标志:

    case <-time.After(TimeoutInterval):
        mu.Lock()
        defer mu.Unlock()
        ch.Lock()
        defer ch.Unlock()
        delete(msgChMap, index)
        ch.active=false
        return ""

当然,有了这个,您现在必须在地图中保留 *channel

您可以通过对发件人使用 select 来防止在等待侦听器时卡住。

在这种情况下,通过使用 select,您可以为发件人使用更多 case

mu.Lock()
msgCh, ok := msgChMap[index]
delete(msgChMap, index)
mu.Unlock()
if ok {
    select {
    // listener is available
    case msgCh <- "yay":
        fmt.Println("sent")

    // if not avalable (execute immediately)
    default:
        fmt.Println("no available listener")
        // ...just ignore or do something else
    }
}

或稍等片刻

mu.Lock()
msgCh, ok := msgChMap[index]
delete(msgChMap, index)
mu.Unlock()
if ok {
    select {
    // listener is available
    case msgCh <- "yay":
        fmt.Println("sent")

    // if not available, waiting for listener
    case <-time.After(30 * time.Second):
        fmt.Println("after 30 seconds, still no available listener")
        // ...just ignore or do something else
    }
}