一尘不染

WaitGroup.Wait()的超时

go

将超时分配给WaitGroup.Wait()的惯用方式是什么?

我要这样做的原因是为了保护我的“调度员”免于永远等待错误的“工人”。这就引出了一些哲学问题(例如,一旦有错误的工作人员,系统如何才能可靠地继续?),但我认为这超出了这个问题的范围。

我将提供一个答案。现在,我已将其写下来,它似乎还不错,但仍然感觉比应有的更复杂。我想知道是否有一些更简单,更惯用的方法,甚至是不使用WaitGroups的替代方法。

助教。


阅读 382

收藏
2020-07-02

共1个答案

一尘不染

通常,您在下面发布的解决方案都可以得到最好的解决方案。改善的几个技巧:

  • 或者,您可以关闭通道以完成信号,而不是在通道上发送值,关闭通道上的接收操作始终可以立即进行
  • 并且最好使用defer语句来表示完成,即使函数突然终止,它也会被执行。
  • 同样,如果只有一个“作业”要等待,则可以完全省略,WaitGroup而仅在作业完成时发送一个值或关闭通道(在select语句中使用相同的通道)。
  • 指定持续1秒很简单,只要:timeout := time.Second。例如,指定2秒为:timeout := 2 * time.Second。您不需要转换,time.Second它已经是type了time.Duration,将其与未类型化的常量相乘,就像这样,2也会产生type的值time.Duration

我还将创建包装此功能的帮助程序/实用程序功能。请注意,WaitGroup必须将其作为指针传递,否则副本将不会“通知”
WaitGroup.Done()调用。就像是:

// waitTimeout waits for the waitgroup for the specified max timeout.
// Returns true if waiting timed out.
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
    c := make(chan struct{})
    go func() {
        defer close(c)
        wg.Wait()
    }()
    select {
    case <-c:
        return false // completed normally
    case <-time.After(timeout):
        return true // timed out
    }
}

使用它:

if waitTimeout(&wg, time.Second) {
    fmt.Println("Timed out waiting for wait group")
} else {
    fmt.Println("Wait group finished")
}

Go Playground上尝试一下。

2020-07-02