为什么在使用`go test`时,`WaitGroup.Wait()`会出现阻塞?

huangapple go评论117阅读模式
英文:

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

问题

这是一个简单的示例,展示了我所说的情况。当我运行这段代码时,我得到以下结果:

  1. === RUN TestWaitGroup
  2. main.go:18: 等待退出信号通道...
  3. main.go:23: 睡眠结束
  4. main.go:25: 关闭退出信号通道
  5. main.go:20: 收到信号
  6. main.go:14: 完成...
  7. main.go:16: 完成!

它会一直挂起,直到超时。如果只使用defer wg.Done(),会观察到相同的行为。我正在运行go1.18。这是一个 bug 还是我在这个上下文中没有正确使用 WaitGroup?

英文:

Here's a simple example of what I mean

  1. package main
  2. import (
  3. "sync"
  4. "testing"
  5. "time"
  6. )
  7. func TestWaitGroup(t *testing.T) {
  8. var wg sync.WaitGroup
  9. quitSig := make(chan struct{})
  10. go func(wg sync.WaitGroup, quitChan, chan struct{}) {
  11. defer func() {
  12. t.Log("Done...")
  13. wg.Done()
  14. t.Log("Done!")
  15. }()
  16. t.Log("waiting for quit channel signal...")
  17. <-quitChan
  18. t.Log("signal received")
  19. }(wg, quitSig)
  20. time.Sleep(5*time.Second)
  21. t.Log("Done sleeping")
  22. close(quitSig)
  23. t.Log("closed quit signal channel")
  24. wg.Wait()
  25. t.Log("goroutine shutdown")
  26. }

When I run this, I get the following

  1. === RUN TestWaitGroup
  2. main.go:18: waiting for quit channel signal...
  3. main.go:23: Done sleeping
  4. main.go:25: closed quit signal channel
  5. main.go:20: signal received
  6. main.go:14: Done...
  7. main.go:16: Done!

Where it just hangs until it timesout. If you just do defer wg.Done() the same behaviour is observed. I'm running go1.18. Is this a bug or am I using not using WaitGroups properly in this context?

答案1

得分: 2

两个问题:

  • 不要复制sync.WaitGroup:根据文档

    • WaitGroup在第一次使用后不能被复制。
  • 在启动工作之前,需要添加wg.Add(1) - 与wg.Done()配对使用


  1. wg.Add(1) // <- 添加这一行
  2. go func(wg *sync.WaitGroup ...) { // <- 指针
  3. }(&wg, quitSig) // <- 使用指针避免复制WaitGroup

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

英文:

Two issues:

  • don't copy sync.WaitGroup: from the docs:

    • A WaitGroup must not be copied after first use.
  • you need a wg.Add(1) before launching your work - to pair with the wg.Done()


  1. wg.Add(1) // &lt;- add this
  2. go func (wg *sync.WaitGroup ...) { // &lt;- pointer
  3. }(&amp;wg, quitSig) // &lt;- pointer to avoid WaitGroup copy

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

答案2

得分: 0

你正在传递一个 waitgroup 的副本,所以 goroutine 不会影响外部作用域中声明的 waitgroup。通过以下方式修复:

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

在调用 goroutine 时,传递指向 waitgroup 的指针,而不是副本。

英文:

You are passing a copy of the waitgroup, so the goroutine does not affect the waitgroup declared in the outer scope. Fix it by:

  1. go func(wg *sync.WaitGroup, quitChan, chan struct{}) {
  2. ...
  3. }(&amp;wg, quitSig)

huangapple
  • 本文由 发表于 2022年4月7日 02:36:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/71772038.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定