为什么 WaitGroup.Wait() 在与 go test 一起使用时会挂起?

Why is WaitGroup.Wait() hanging when using it with go test?

这是我的意思的一个简单例子

package main

import (
    "sync"
    "testing"
    "time"
)

func TestWaitGroup(t *testing.T) {
    var wg sync.WaitGroup
    quitSig := make(chan struct{})
    go func(wg sync.WaitGroup, quitChan, chan struct{}) {
        defer func() {
            t.Log("Done...")
            wg.Done()
            t.Log("Done!")
        }()
        t.Log("waiting for quit channel signal...")
        <-quitChan
        t.Log("signal received")
    }(wg, quitSig)
    time.Sleep(5*time.Second)
    t.Log("Done sleeping")
    close(quitSig)
    t.Log("closed quit signal channel")
    wg.Wait()
    t.Log("goroutine shutdown")
}

当我运行这个时,我得到以下

=== RUN   TestWaitGroup
    main.go:18: waiting for quit channel signal...
    main.go:23: Done sleeping
    main.go:25: closed quit signal channel
    main.go:20: signal received
    main.go:14: Done...
    main.go:16: Done!
    

它只是挂起,直到超时。如果您只是执行 defer wg.Done(),则会观察到相同的行为。我 运行宁 go1.18。这是一个错误还是我在这种情况下没有正确使用 WaitGroups?

您正在传递等待组的副本,因此 goroutine 不会影响在外部作用域中声明的等待组。通过以下方式修复:

    go func(wg *sync.WaitGroup, quitChan, chan struct{}) {
     ...
    }(&wg, quitSig)

两期:

  • 不要复制 sync.WaitGroup: 来自 docs:

    • A WaitGroup must not be copied after first use.
  • 您需要 wg.Add(1) 才能开始您的工作 - 与 wg.Done()

    配对

wg.Add(1) // <- add this

go func (wg *sync.WaitGroup ...) { // <- pointer
}(&wg, quitSig) // <- pointer to avoid WaitGroup copy

https://go.dev/play/p/UmeI3TdGvhg