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

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

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

问题

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

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

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

英文:

Here's a simple example of what I mean

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")
}

When I run this, I get the following

=== 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!
    

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()配对使用


wg.Add(1) // <- 添加这一行

go func(wg *sync.WaitGroup ...) { // <- 指针
}(&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()


wg.Add(1) // &lt;- add this

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

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

答案2

得分: 0

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

go func(wg *sync.WaitGroup, quitChan chan struct{}) {
    ...
}(&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:

    go func(wg *sync.WaitGroup, quitChan, chan struct{}) {
     ...
    }(&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:

确定