Go語言中的併發知識點彙總

Go語言是一種併發語言,不是並行語言,併發和並行的區別,這裡就不說了,Go語言使用協程來實現併發,

Go協程與執行緒的區別

協程

的成本極低,執行時只有

kb

大小

協程

會複用數量更少的

OS

執行緒

Go

協程使用通道來進行通訊

Go關鍵字啟動一個協程

package main

import (

“fmt”

“time”

func hello() {

fmt。Println(“go協程列印”)

}

func main() {

go hello() //使用go關鍵字,啟用一個協程

time。Sleep(time。Second) //如果沒有這個是不會列印協程裡的資料的,原因函式並不會主動等待協程呼叫完成,導致協程並沒有執行呢,

// 主程式就執行結束了,所以打印不出來了!

fmt。Println(“我是 main”)

}

使用chan通道用於多個協程間的通訊

建立通道

//建立通道

package main

import “fmt”

func main() {

//使用 chan T 來宣告T型別的通道,建立通道,只能使用make()來建立,否則它的空值是nil,無法使用

//numchan :=make(chan int)//這個建立了一個無緩衝的通道

numchan :=make(chan int ,10)//這個建立了一個有緩衝的通道

fmt。Println(numchan)

}

//有緩衝通道與無緩衝通道的區別

有緩衝通道:不僅可以傳遞資料,並可以保留緩衝大小的通道,在緩衝足夠大時,不要求通道讀操作與通道寫操作同時執行

無緩衝通道:只有傳輸資料的功能,並且通道寫操作與通道讀操作需要同步執行,否則會出現死鎖操作

通道的傳送與接收

通道的傳送與接收是預設阻塞的,當資料傳送到通道時,程式會在傳送資料的語句處發生阻塞,直到有其它go協程從通道讀取到資料,都會解除阻塞,同理,當讀取通道中的資料也是一樣的

//寫入通道 numchan<- //numchan是上面定義的通道//讀取通道 <-numchanpackage mainimport “fmt”func hello2(c chan int) { fmt。Println(“go協程列印”) c<-1}func main() { //使用 chan T 來宣告T型別的通道,建立通道,只能使用make()來建立,否則無法使用 //numchan :=make(chan int)//這個建立了一個無緩衝的通道 numchan :=make(chan int ,10)//這個建立了一個有緩衝的通道 //這樣寫沒有意思,這裡只是演示 //把1放到通道中 data :=<-numchan //從通道中讀取資料 fmt。Println(data)}

單向通道

雙向通道可以轉向單向通道,單向通道不能變成雙向通道

package mainimport “fmt”func main() { numchan :=make(chan<- int) //這裡建立一個只寫的單向通道 numchan<-1 fmt。Println(“這裡會報錯死鎖,原因沒有讀”)}//但是在實際工作中沒有這樣的宣告,一般都會宣告一個雙向通道,在使用時才定義接收的型別是隻讀或只寫的通道型別package mainimport ( “fmt”)//定義一個只接收只寫int型別的通道func hello3( c chan<- int ) { c<-1 fmt。Println(“只寫”)}//定義一個只讀的int型別的通道func hello4(c <-chan int ) { data :=<-c fmt。Println(“只讀執行緒”,data)}func main() { numchan :=make(chan int) //這裡建立一個只寫的單向通道 go hello3(numchan) data :=<-numchan fmt。Println(data)}

通道的遍歷

使用

for range

進行通道的遍歷

使用

close()

函式進行通道的關閉

關閉通道操作 應該在寫操作中完成

package main

import “fmt”

func getdata(c chan int) {

for i := 0; i < 10; i++ {

c<-i

}

close(c) //關閉通道

}

func main() {

numchan :=make(chan int)

go getdata(numchan)

//使用for 來遍歷通道

for{

data ,ok:=<-numchan //如果關閉通道後,ok會返回false,否則是true

if !ok{

fmt。Println(“通道已經關閉”,ok)

return

}

fmt。Println(“信道里的資料”,data)

}

//還可以使用for range 來遍歷通道

for v:= range numchan {//注意這裡只有返回一個返回值

fmt。Println(“讀取通道中的值”,v)

}

}

WaitGroup利用計數器解決多協程與主執行緒之間同步的問題

package main

import (

“fmt”

“sync”

“time”

func wg1(i int , wg *sync。WaitGroup) {

fmt。Println(“開始”)

time。Sleep(time。Second*2)

wg。Done() //計數器減1

fmt。Println(“計算器減1”)

}

func main() {

var wg sync。WaitGroup //定義一個WaitGroup型別的變數計數器

//開啟10的協程

for i := 0; i < 10; i++ {

wg。Add(1) //計數器加1

go wg1(i,&wg) //注意這裡是傳的是地址,如果不傳的是地址,多個協程會建立自己的副本,效果並不會出現

}

wg。Wait() //用於阻塞主執行緒 當計數器減到0,則結束等待

}

利用select語句來監聽多個通道的讀取

select語句會一直阻塞,直接通道中有資料,如果多個通道有資料,select會隨機選擇其中一 個執行

package mainimport ( “fmt” “time”)func hello11(c1 chan int) { c1 <- 1}func main() { c1 := make(chan int) go hello11(c1) for{ time。Sleep(time。Second) select { case v :=<-c1: fmt。Println(“資料”,v) return default: fmt。Println(“沒有資料”) } }}

空select語句

空select語句,會阻塞,導致panic,死鎖