Golang中使用WaitGroup时所有goroutine都处于休眠状态的问题

Golang中使用WaitGroup时所有goroutine都处于休眠状态的问题 我正在探索如何使用 WaitGroup 来等待所有 goroutine 完成。这是我的代码:

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(3)

	go one(wg)
	go two(wg)
	go three(wg)

	wg.Wait()
}

func one(wg sync.WaitGroup) {
	defer wg.Done()
	time.Sleep(5 * time.Second)
	fmt.Println("One")
}

func two(wg sync.WaitGroup) {
	defer wg.Done()
	time.Sleep(7 * time.Second)
	fmt.Println("Two")
}

func three(wg sync.WaitGroup) {
	defer wg.Done()
	time.Sleep(3 * time.Second)
	fmt.Println("Three")
}

在所有 goroutine 都打印完毕后,我一直收到这个错误“所有 goroutine 都已休眠 - 死锁”。我无法理解这里出了什么问题——没有涉及任何通道——这只是一个简单的流程。我遗漏了什么?


更多关于Golang中使用WaitGroup时所有goroutine都处于休眠状态的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

谢谢。

更多关于Golang中使用WaitGroup时所有goroutine都处于休眠状态的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你正在通过值复制 WaitGroup,你需要通过引用来传递它,使用 wg := new(sync.WaitGroup) 是惯用的做法。

你的代码中存在一个关键问题:sync.WaitGroup 应该通过指针传递,而不是值传递。当你将 WaitGroup 作为值传递给 goroutine 函数时,每个 goroutine 都会获得该 WaitGroup 的副本,导致主 goroutine 中的原始 WaitGroup 永远不会收到 Done() 调用,从而造成死锁。

以下是修正后的代码:

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(3)

	go one(&wg)
	go two(&wg)
	go three(&wg)

	wg.Wait()
	fmt.Println("All goroutines completed")
}

func one(wg *sync.WaitGroup) {
	defer wg.Done()
	time.Sleep(5 * time.Second)
	fmt.Println("One")
}

func two(wg *sync.WaitGroup) {
	defer wg.Done()
	time.Sleep(7 * time.Second)
	fmt.Println("Two")
}

func three(wg *sync.WaitGroup) {
	defer wg.Done()
	time.Sleep(3 * time.Second)
	fmt.Println("Three")
}

关键修改:

  1. 将函数签名改为接收 *sync.WaitGroup 指针类型
  2. 在调用 goroutine 时传递 &wg 地址
  3. 所有 goroutine 现在操作的是同一个 WaitGroup 实例

运行结果:

Three
One
Two
All goroutines completed

另一种常见的写法是使用闭包来避免传递 WaitGroup:

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup
	
	tasks := []func(){
		func() {
			time.Sleep(5 * time.Second)
			fmt.Println("One")
		},
		func() {
			time.Sleep(7 * time.Second)
			fmt.Println("Two")
		},
		func() {
			time.Sleep(3 * time.Second)
			fmt.Println("Three")
		},
	}

	wg.Add(len(tasks))
	
	for _, task := range tasks {
		go func(t func()) {
			defer wg.Done()
			t()
		}(task)
	}
	
	wg.Wait()
	fmt.Println("All goroutines completed")
}

这种闭包方式让代码更简洁,同时避免了参数传递的问题。

回到顶部