【Go學習筆記12】併發(二)

WaitGroup介紹

在go語言中,WaitGroup是用來表示一組等待goroutine運行結束的集合。你能夠在主goroutine中使用Add方法添加須要等待的goroutine數量,在其它的goroutine結束的時候,須要調用Done方法來通知已經執行結束。同時,Wait方法會阻塞代碼的執行直到全部的goroutine都執行完成。官方文檔請點函數

這裏

使用WaitGroup等待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) //將value1 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函數執行完成 複製代碼

上面的代碼模擬了生產者消費者模式,生產者往通道發送數據,而消費者從通道中獲取數據,當生產者生產完成以後,關閉通道。

相關文章
相關標籤/搜索