在Go语言的并发编程场景中,sync.WaitGroup常被用来等待一组协程完成任务,部分场景下我们需要获取并打印它的指针地址来辅助调试,但是很多开发者会发现直接打印的结果和自己预期的不一致,这背后涉及sync.WaitGroup的内部设计和Go语言的指针特性。

sync.WaitGroup的基础认知
sync.WaitGroup是Go语言标准库sync包提供的同步原语,主要包含三个方法:Add、Done、Wait。它的作用是等待一组协程执行完成,内部通过一个计数器来记录未完成的任务数量。当计数器归零时,所有阻塞在Wait方法的协程会被唤醒。
我们通常会将sync.WaitGroup作为参数传递给多个协程,这时候传递的方式会影响指针地址的获取。如果是值传递,每个协程拿到的是副本,修改计数器不会相互影响;如果是指针传递,多个协程操作的是同一个实例,这才是正确的使用方式。
指针地址的获取与打印问题
很多开发者尝试打印sync.WaitGroup的指针地址时,会写出类似下面的代码:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
// 尝试打印wg的指针地址
fmt.Printf("wg指针地址: %pn", &wg)
}
这段代码的输出看起来是正常的,但是如果我们将wg作为参数传递给函数,再尝试打印,就可能出现疑惑:
package main
import (
"fmt"
"sync"
)
func printAddr(wg sync.WaitGroup) {
// 这里打印的是wg副本的地址
fmt.Printf("函数内wg指针地址: %pn", &wg)
}
func main() {
var wg sync.WaitGroup
fmt.Printf("主函数wg指针地址: %pn", &wg)
printAddr(wg)
}
运行后会发现两个地址不一样,这是因为printAddr函数接收的是wg的值副本,并不是原始实例的指针。如果我们想要在函数内获取原始实例的地址,需要传递指针:
package main
import (
"fmt"
"sync"
)
func printAddr(wg *sync.WaitGroup) {
// 这里打印的是原始实例的指针地址
fmt.Printf("函数内wg指针地址: %pn", wg)
}
func main() {
var wg sync.WaitGroup
fmt.Printf("主函数wg指针地址: %pn", &wg)
printAddr(&wg)
}
深度解析:为什么地址打印要这样处理
sync.WaitGroup的内部结构是一个包含计数器和信号量的结构体,Go语言规定,所有的同步原语都不应该被复制,因为复制后的副本和原始实例的状态是独立的,会导致同步逻辑失效。因此官方推荐始终使用指针传递sync.WaitGroup。
当我们使用%p格式化符打印地址时,%p期望接收的是一个指针类型的值。如果传递的是&wg,那么得到的是wg实例的内存地址;如果传递的是wg指针变量,那么%p会直接打印这个指针变量存储的地址,也就是原始实例的地址。
需要注意的是,不要尝试打印sync.WaitGroup内部字段的地址,因为它的内部字段是私有的,外部无法直接访问,而且它的内存布局可能会随着Go版本的迭代发生变化,依赖内部字段地址是不稳定的做法。
正确的打印实践总结
- 始终使用指针传递sync.WaitGroup,避免值拷贝导致的同步问题
- 打印指针地址时,要么直接打印指针变量的值,要么打印实例的
&取地址结果 - 不要在函数内对值传递的sync.WaitGroup取地址,得到的只是副本的地址,没有实际意义
- 避免使用fmt.Sprintf等函数拼接地址字符串时,错误传递参数类型导致结果异常
下面是一个完整的正确实践示例:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("协程%d开始执行,wg指针地址: %pn", id, wg)
time.Sleep(time.Second)
fmt.Printf("协程%d执行完成n", id)
}
func main() {
var wg sync.WaitGroup
fmt.Printf("主函数wg指针地址: %pn", &wg)
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("所有协程执行完成")
}
运行这段代码可以看到,所有协程打印的wg指针地址和主函数中的地址是一致的,说明所有协程操作的是同一个sync.WaitGroup实例,同步逻辑可以正常工作。
sync.WaitGroupGo语言指针地址打印实践深度解析修改时间:2026-07-05 08:57:10