Golang sync.WaitGroup的用法

0x01 介紹

常常會看到如下了代碼:函數

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
"time"
)

func main(){
for i := 0; i < 100 ; i++{
go fmt.Println(i)
}
time.Sleep(time.Second)
}

主線程爲了等待goroutine都運行完畢,不得不在程序的末尾使用time.Sleep() 來睡眠一段時間,等待其餘線程充分運行。對於簡單的代碼,100個for循環能夠在1秒以內運行完畢,time.Sleep() 也能夠達到想要的效果。工具

可是對於實際生活的大多數場景來講,1秒是不夠的,而且大部分時候咱們都沒法預知for循環內代碼運行時間的長短。這時候就不能使用time.Sleep() 來完成等待操做了。ui

能夠考慮使用管道來完成上述操做:spa

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
c := make(chan bool, 100)
for i := 0; i < 100; i++ {
go func(i int) {
fmt.Println(i)
c <- true
}(i)
}

for i := 0; i < 100; i++ {
<-c
}
}

首先能夠確定的是使用管道是能達到咱們的目的的,並且不但能達到目的,還能十分完美的達到目的。線程

可是管道在這裏顯得有些大材小用,由於它被設計出來不單單只是在這裏用做簡單的同步處理,在這裏使用管道其實是不合適的。並且假設咱們有一萬、十萬甚至更多的for循環,也要申請一樣數量大小的管道出來,對內存也是不小的開銷。設計

對於這種狀況,go語言中有一個其餘的工具sync.WaitGroup 能更加方便的幫助咱們達到這個目的。指針

WaitGroup 對象內部有一個計數器,最初從0開始,它有三個方法:Add(), Done(), Wait() 用來控制計數器的數量。Add(n) 把計數器設置爲n ,Done() 每次把計數器-1 ,wait() 會阻塞代碼的運行,直到計數器地值減爲0。code

使用WaitGroup 將上述代碼能夠修改成:對象

1
2
3
4
5
6
7
8
9
10
11
func main() {
wg := sync.WaitGroup{}
wg.Add(100)
for i := 0; i < 100; i++ {
go func(i int) {
fmt.Println(i)
wg.Done()
}(i)
}
wg.Wait()
}

這裏首先把wg 計數設置爲100, 每一個for循環運行完畢都把計數器減一,主函數中使用Wait() 一直阻塞,直到wg爲零——也就是全部的100個for循環都運行完畢。相對於使用管道來講,WaitGroup 輕巧了許多。進程

0x02 注意事項

1. 計數器不能爲負值

咱們不能使用Add() 給wg 設置一個負值,不然代碼將會報錯:

1
2
3
4
5
6
7
panic: sync: negative WaitGroup counter

goroutine 1 [running]:
sync.(*WaitGroup).Add(0xc042008230, 0xffffffffffffff9c)
D:/Go/src/sync/waitgroup.go:75 +0x1d0
main.main()
D:/code/go/src/test-src/2-Package/sync/waitgroup/main.go:10 +0x54

一樣使用Done() 也要特別注意不要把計數器設置成負數了。

2. WaitGroup對象不是一個引用類型

WaitGroup對象不是一個引用類型,在經過函數傳值的時候須要使用地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
wg := sync.WaitGroup{}
wg.Add(100)
for i := 0; i < 100; i++ {
go f(i, &wg)
}
wg.Wait()
}

// 必定要經過指針傳值,否則進程會進入死鎖狀態
func f(i int, wg *sync.WaitGroup) {
fmt.Println(i)
wg.Done()
}
相關文章
相關標籤/搜索