Skip to the content.

概述

正常情况下 goroutine的结束过程是不可控制的,唯一可以保证终止goroutine的行为是main goroutine的终止。也就是说,我们并不知道哪个goroutine什么时候结束。 通过WaitGroup提供的三个函数:Add,Done,Wait,可以轻松实现等待某个协程或协程组完成的同步操作

WatiGroup

WatiGroup是sync包中的一个struct类型,用来收集需要等待执行完成的goroutine

// 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.
//
// A WaitGroup must not be copied after first use.
type WaitGroup struct {
	noCopy noCopy
	state1 [3]uint32
}

WaitGroup结构一旦定义就不能复制

主要函数

func (wg *WaitGroup) Add(delta int)

Add方法增加WaitGroup计数器的值,注意Add加上正数的调用应在Wait之前,delta可以是负数;如果内部计数器变为0,Wait方法阻塞等待的所有协程都会释放,如果计数器小于0,则调用panic

func (wg *WaitGroup) Done()

每次需要等待的goroutine在真正完成之前,应该调用该方法来人为表示goroutine完成了,该方法会对等待计数器减1

func (wg *WaitGroup) Wait()

Wait方法阻塞直到WaitGroup计数器减为0

用例

利用 defer 在协程完成前减一

func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("1 goroutine sleep ...")
		time.Sleep(2e9)
		fmt.Println("1 goroutine exit ...")
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("2 goroutine sleep ...")
		time.Sleep(4e9)
		fmt.Println("2 goroutine exit ...")
	}()

	fmt.Println("waiting for all goroutine ")
	wg.Wait()
	fmt.Println("All goroutines finished!")
}

执行结果:

waiting for all goroutine 
2 goroutine sleep ...
1 goroutine sleep ...
1 goroutine exit ...
2 goroutine exit ...
All goroutines finished!

waitgroup 指针类型做函数参数为

func main() {
	sayHello := func(wg *sync.WaitGroup, id int) {
		defer wg.Done()
		fmt.Printf("%v goroutine start ...\n", id)
		time.Sleep(2 * time.Second)
		fmt.Printf("%v goroutine exit ...\n", id)
	}

	var wg sync.WaitGroup
	const N = 5
	wg.Add(N)
	for i := 0; i < N; i++ {
		go sayHello(&wg, i)
	}

	fmt.Println("waiting for all goroutine ")
	wg.Wait()
	fmt.Println("All goroutines finished!")
}

执行结果:

waiting for all goroutine 
4 goroutine start ...
3 goroutine start ...
1 goroutine start ...
2 goroutine start ...
0 goroutine start ...
1 goroutine exit ...
4 goroutine exit ...
0 goroutine exit ...
2 goroutine exit ...
3 goroutine exit ...
All goroutines finished!

官方

func main() {
    var wg sync.WaitGroup
    var urls = []string{
            "http://www.golang.org/",
            "http://www.google.com/",
            "http://www.baidu.com/",
    }
    for _, url := range urls {
        // Increment the WaitGroup counter.
        wg.Add(1)
        // Launch a goroutine to fetch the URL.
        go func(url string) {
            // Decrement the counter when the goroutine completes.
            defer wg.Done()
            // Fetch the URL.
            http.Get(url)
        fmt.Println(url);
        }(url)
    }
    // Wait for all HTTP fetches to complete.
    wg.Wait()
    fmt.Println("over");
}

执行结果:

http://www.baidu.com/
http://www.google.com/
http://www.golang.org/
over

总结