go WaitGroup

官网说明

A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished.

WaitGroup 同步的是 go 协程

示例

示例一

package main
import (
	"fmt"
	"sync"
)
func main(){
	wg := sync.WaitGroup{}
	for i := 0; i < 8; i++ {
		go func(i int){
			wg.Add(1)
			defer wg.Done()
			fmt.Println("the %d goroutine\n",i)	
		}(i)
	}
	wg.Wait()
	fmt.Println("main exit...")
}

在执行上面的代码后,会出现以下情况:
情况一:

# go run mywait.go
the 7 goroutine
the 6 goroutine
the 0 goroutine
the 2 goroutine
the 3 goroutine
the 1 goroutine
the 4 goroutine
the 5 goroutine
main exit...

情况二:

# go run mywait.go
main exit...

情况三:

# go run mywait.go
the 1 goroutine
the 4 goroutine
the 7 goroutine
the 6 goroutine
the 3 goroutine
the 2 goroutine
the 0 goroutine
the 5 goroutine
panic: sync: WaitGroup is reused before previous Wait has returned

goroutine 1 [running]:
sync.(*WaitGroup).Wait(0xc000016090)
	/usr/local/go/src/sync/waitgroup.go:132 +0xad
main.main()
	/Users/goproj/src/person/mywait.go:17 +0x83
exit status 2

根据开头的英文解释,WaitGroup 是同步 goroutine 的。在 go 协程内调用 wg.Add(1) 并不一定会被 main 流程感知。会出现 go协和还未被调度,主协程已经运行到 wg.Wait() 而直接终止,或像情况三那样,在调用 wg.Wait() 之后,还未执行打印,就又调度 go 协程,在协程中执行 wg.Add(1) 时,而此时 Wait() 已经返回了,就会抛出一个 panic

示例二

再来看下面的例子

package main
import (
	"fmt"
	"sync"
)
func main(){
	wg := sync.WaitGroup{}
	for i := 0; i < 8; i++ {
		wg.Add(1)
		go func(i int,wg sync.WaitGroup){
			defer wg.Done()
			fmt.Println("the %d goroutine\n",i)	
		}(i,wg)
	}
	wg.Wait()
	fmt.Println("main exit...")
}

打印如下:

go run mywait.go
the 7 goroutine
the 5 goroutine
the 6 goroutine
the 2 goroutine
the 3 goroutine
the 0 goroutine
the 1 goroutine
the 4 goroutine
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc000016098)
	/usr/local/go/src/runtime/sema.go:56 +0x39
sync.(*WaitGroup).Wait(0xc000016090)
	/usr/local/go/src/sync/waitgroup.go:130 +0x64
main.main()
	/Users/goproj/src/person/mywait.go:17 +0xab
exit status 2

此时传递给 go 协程的是 wg 的副本,调用 defer wg.Done()wg.Add(1)并不是同一个 wg
解决方法是将传值改为传指针。

示例三 值传递 and 指针传递

值传递传递的是变量的副本 ,指针传递传递的就是变量本身吗?
请看示例

package main

import (
	"fmt"
	"sync"
)

func main(){
	wg := &sync.WaitGroup{}
	for i := 0; i < 2; i++ {
		fmt.Printf("out wg memory address:%p,wg pointer address:%p\n",wg,&wg)
		fmt.Printf("out i:%p,*i:%p\n",i,&i)
		wg.Add(1)
		go func(wg *sync.WaitGroup,i int){
			defer wg.Done()
			fmt.Printf("inner wg memory address:%p,wg pointer address:%p\n",wg,&wg)
			fmt.Printf("inner i:%p,*i:%p\n",i,&i)
		}(wg,i)
	}
	wg.Wait()
	fmt.Printf("main exit...\n")
}

打印如下:

# go run mywait.go
out wg memory address:0xc000016090,wg pointer address:0xc00000c028
out i:%!p(int=0),*i:0xc0000160a0
out wg memory address:0xc000016090,wg pointer address:0xc00000c028
out i:%!p(int=1),*i:0xc0000160a0
inner wg memory address:0xc000016090,wg pointer address:0xc00000c038
inner i:%!p(int=1),*i:0xc0000160b0
inner wg memory address:0xc000016090,wg pointer address:0xc00008a000
inner i:%!p(int=0),*i:0xc00008c000

从上面的打印可以看出,go语言中指针传递,传递的仍是变量的副本 ,只不过变量是指针,传递的是指针的副本,但指针指向的内容是同一块内存的内容。

参考:

  1. Golang中WaitGroup使用的一点坑
  2. []T 还是 []*T, 这是一个问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值