在go語言中,WaitGroup是用來表示一組等待goroutine運行結束的集合。你能夠在主goroutine中使用Add方法添加須要等待的goroutine數量,在其它的goroutine結束的時候,須要調用Done方法來通知已經執行結束。同時,Wait方法會阻塞代碼的執行直到全部的goroutine都執行完成。官方文檔請點函數
在上一篇文章中,咱們使用了time.Sleep來阻塞主goroutine的執行,等待其它goroutine執行結束。如今咱們來使用WaitGroup來實現:spa
package main
import (
"sync"
"fmt"
)
//使用WaitGroup來等待goroutine結束
var wait sync.WaitGroup
func A(){
fmt.Println("A執行完成。。")
wait.Done()
}
func main(){
//增長1,表示須要等待一個goroutine執行結束
wait.Add(1)
go A()
//等待goroutine結束
wait.Wait()
fmt.Println("main函數執行完成")
}
//A執行完成。。
//main函數執行完成
複製代碼
能夠看到使用了上面的代碼來等待goroutine完成更加準確,而不像time.Sleep那樣可能會出現等待時間以後goroutine尚未結束或者等待時間過長的問題。code
從上面的代碼,咱們能夠能夠看到WaitGroup實際上是使用了信號量的機制來實現的。cdn
在go語言中,通道被用來在多個goroutine之間同步數據。在上一篇文章中介紹的原子操做和同步鎖解決數據爭用的問題。一樣可使用通道來解決,並且通道更加方便,容易使用。當須要在多個goroutine中共享資源的時候,通道能夠在goroutine之間傳遞數據,而且能夠確保這個過程是同步的。而通道又分爲有無緩衝通道和有緩衝通道。xml
無緩衝通道在收到數據時當即傳遞給另一個goroutine,不會緩衝數據。這就須要發送數據的goroutine和接收數據的goroutine同時準備好。不然就會阻塞。資源
//建立一個無緩衝通道
buffer := make(chan int)
複製代碼
使用內置的make函數來建立通道,第一個參數須要使用關鍵字chan,以後是該通道能夠傳遞的數據類型。上面的例子表示建立了一個能夠傳遞int類型數據的無緩衝通道。文檔
package main
import (
"sync"
"fmt"
)
func main(){
//建立一個WaitGroup
var wait sync.WaitGroup
//信號量+2,表示等待兩個goroutine完成
wait.Add(2)
buffer := make(chan int)
go func(){
fmt.Println("將數據放入通道。")
//將12放入通道
buffer <- 12
//完成
wait.Done()
}()
go func(){
//從通道中獲取數據
fmt.Println("從通道拿到數據:",<- buffer)
//關閉通道
close(buffer)
//完成
wait.Done()
}()
//等待兩個goroutine完成
wait.Wait()
fmt.Println("main函數執行完成。")
}
//將數據放入通道。
//從通道拿到數據: 12
//main函數執行完成。
複製代碼
上面的代碼,使用通道將數據從一個goroutine傳遞到了另一個goroutine。咱們也能夠在兩個goroutine之間相互傳遞數據。來看下面的代碼:同步
package main
import (
"sync"
"fmt"
)
func main(){
//建立一個WaitGroup
var wait sync.WaitGroup
//信號量+2,表示等待兩個goroutine完成
wait.Add(2)
buffer := make(chan int)
go func(name string){
fmt.Println("將數據放入通道。")
//將12放入通道
buffer <- 12
fmt.Println(name,"從通道拿到數據:",<-buffer)
//完成
wait.Done()
}("A")
go func(name string){
//從通道中獲取數據
value := <- buffer fmt.Println(name,"從通道拿到數據:",value) //將value加1 value++ //從新放入通道 buffer <- value //完成 wait.Done() }("B") //等待兩個goroutine完成 wait.Wait() //關閉通道 close(buffer) fmt.Println("main函數執行完成。") } //將數據放入通道。 //B 從通道拿到數據: 12 //A 從通道拿到數據: 13 //main函數執行完成。 複製代碼
從上面的代碼執行結果能夠看出,咱們能夠在兩個goroutine之間交換數據,而且通道保證了數據的同步。string
有緩衝通道表示,在通道接收到數據的時候會保留到緩衝區,等待接收goroutine的讀取。只有在緩衝區滿了以後,發送goroutine纔會阻塞,而只有緩衝區無數據的時候,接收goroutine纔會阻塞。it
//建立一個有緩衝通道,緩衝區的大小爲10
buffer := make(chan int,10)
複製代碼
上面的代碼使用內置函數make建立了一個有緩衝的通道,緩衝的大小爲10。
package main
import (
"time"
"sync"
"fmt"
)
var wait sync.WaitGroup
func Producer(channel chan int){
for i:= 1 ; i<10 ;i++{
//休眠100毫秒
time.Sleep(100 * time.Millisecond)
//向通道中發送數據
channel <- i
fmt.Println("生產者生產:",i)
}
//生產完成,關閉通道
close(channel)
wait.Done()
}
func Consumer(channel chan int){
//延遲執行
defer wait.Done()
for {
//從通道中讀取數據
value ,ok := <- channel //當通道關閉的時候返回 if !ok { return } //休眠150毫秒 time.Sleep(150 * time.Millisecond) fmt.Println("消費者消費:",value) } } func main(){ //建立一個有緩衝通道,緩衝區的大小爲10 buffer := make(chan int,10) wait.Add(2) go Producer(buffer) go Consumer(buffer) wait.Wait() fmt.Println("mian函數執行完成") } //生產者生產: 1 //生產者生產: 2 //消費者消費: 1 //生產者生產: 3 //消費者消費: 2 //生產者生產: 4 //生產者生產: 5 //消費者消費: 3 //生產者生產: 6 //消費者消費: 4 //生產者生產: 7 //生產者生產: 8 //消費者消費: 5 //生產者生產: 9 //消費者消費: 6 //消費者消費: 7 //消費者消費: 8 //消費者消費: 9 //mian函數執行完成 複製代碼
上面的代碼模擬了生產者消費者模式,生產者往通道發送數據,而消費者從通道中獲取數據,當生產者生產完成以後,關閉通道。