首頁 > 軟體

Go保證並行安全底層實現詳解

2022-09-02 18:02:30

引言

上一部分主要寫了鎖,本篇主要介紹Channel

channel是Go中非常重要的一個資料型別,它和goroutine緊密相連,是Go的CSP並行模型的重要體現。

CSP

  • CSP 是交談循序程式(Communicating Sequential Process)的簡稱,是一種並行程式設計模型。
  • 簡單來說,CSP模型由並行的實體所組成,實體之間通過傳送訊息進行通訊,而傳送訊息使用的就是通道,即channel。
  • GO實現了CSP部分理論,goroutine對應CSP中的並行執行的實體,channel對應CSP中的channel。

不要通過共用記憶體來通訊,而應該通過通訊來共用記憶體

Channel的基本使用

package main
import "fmt"
func main() {
	c := make(chan int)
	go func() {
		c <- 1 // 向channel傳送資料
	}()
	x := <-c // 從channel中接收資料
	fmt.Println(x)
}

1、通過make(chan int)建立一個int channel(可以在channel初始化時指定緩衝區的大小,例如make(chan int,2),不指定則預設為0)

2、在一個goroutine中,通過c<-1將資料傳送到channel中,<-可以理解為資料的流動方向。

3、在主goroutine中通過x := <-c接收channel中的資料,並賦值給x。

channel如何保證並行安全

既然goroutin和channel分別對應csp中的實體和媒介,goroutin之間都是通過chennel來傳遞資料,那麼是如何保證並行安全的呢?

通過閱讀原始碼可以發現,channel內部是使用Mutext互斥鎖來保證的( 之前也有人提出CAS無鎖Channel的實現,但因為無鎖Channel在多核測試中的表現和沒有滿足FIFO的特性等原因,該提案目前是擱淺狀態)關於無鎖channel的討論

channel的底層實現

channel的核心原始碼位於runtime包的chan.go中。

hchan 是 channel 在 golang 中的內部實現

type hchan struct { 
    qcount uint // total data in the queue 
    dataqsiz uint // size of the circular queue 
    buf unsafe.Pointer // points to an array of dataqsiz elements 
    elemsize uint16 
    closed uint32 
    elemtype *_type // element type 
    sendx uint // send index 
    recvx uint // receive index 
    recvq waitq // list of recv waiters 
    sendq waitq // list of send waiters 
    // lock protects all fields in hchan, as well as several 
    // fields in sudogs blocked on this channel. 
    // 
    // Do not change another G's status while holding this lock 
    // (in particular, do not ready a G), as this can deadlock 
    // with stack shrinking. 
    lock mutex 
 } 

hchan的所有屬性大體可以分為3類

1、buffer相關屬性,當channel中的緩衝區大小不為0時,buffer中存放了待接收的資料。

2、waitq相關屬性,即recvq和sendq,可以理解為一個標準的FIFO佇列,recvq是等待接收資料的goroutine,sendq是等待傳送資料的goroutine。

3、其它,例如lock(互斥鎖)、elemtype(元素型別)、closed(channel 是否關閉,== 0 代表未 closed)

hchan的所有行為,基本都是圍繞bufferwaitq來實現的

waitq

type waitq struct { 
 first *sudog 
 last *sudog 
 } 

waitq是一個雙向連結串列,裡面儲存了goroutine。

buffe

buffer使用 ring buffer(環形緩衝區)實現

在hchan中,可以看到 recvxsendx 兩個屬性,recvx即當前已傳送的元素在佇列當中的索引位置,sendx 即 當前已接收的元素在佇列當中的索引位置。

從 recvx 到 sendx 之間的元素,表示已正常存放入 buffer 中的資料。

Lock

hchan中的lock就是一個互斥鎖,channel在傳送和接收資料前,都會先進行加鎖,待邏輯完成後執行再解鎖,來保證並行安全。

以上就是Go保證並行安全底層實現詳解的詳細內容,更多關於Go並行安全底層實現的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com