首页 > 解决方案 > 如何协调关闭与许多 goroutines

问题描述

说我有一个功能

type Foo struct {}

func (a *Foo) Bar() {
    // some expensive work - does some calls to redis
}

在我的应用程序的某个时间点在 goroutine 中执行。其中许多可能在任何给定点执行。在应用程序终止之前,我想确保所有剩余的 goroutine 都完成了它们的工作。

我可以做这样的事情:

type Foo struct {
    wg sync.WaitGroup
}

func (a *Foo) Close() {
    a.wg.Wait()
}

func (a *Foo) Bar() {
    a.wg.Add(1)
    defer a.wg.Done()

    // some expensive work - does some calls to redis
}

这里假设 Bar 在 goroutine 中执行,其中许多可能在给定时间运行,并且一旦调用 Close 并且在 sigterm 或 sigint 上调用 Close,则不应调用 Bar。

这有意义吗?

通常我会看到 Bar 函数如下所示:

func (a *Foo) Bar() {
    a.wg.Add(1)

    go func() {
        defer a.wg.Done()
        // some expensive work - does some calls to redis
    }()
}

标签: go

解决方案


是的,WaitGroup是正确的答案。根据doc ,您可以WaitGroup.Add随时使用计数器大于零的值。

请注意,当计数器为零时发生的具有正增量的调用必须在等待之前发生。具有负增量的调用或具有正增量的调用在计数器大于零时开始,可能随时发生。通常这意味着对 Add 的调用应该在创建 goroutine 的语句或其他要等待的事件之前执行。如果重用 WaitGroup 来等待多个独立的事件集,则必须在所有先前的 Wait 调用都返回后发生新的 Add 调用。请参阅 WaitGroup 示例。

Close但是一个技巧是,在调用之前,您应该始终保持计数器大于零。这通常意味着您应该调用wg.Addin NewFoo(或类似的东西)和wg.Donein Close。并且为了防止多次调用Done破坏等待组,你应该Close换成sync.Once. 您可能还想防止 newBar()被调用。


推荐阅读