[轉]從Deadlock報錯理解Go channel機制

原文: https://www.jianshu.com/p/147bd63801b6安全

--------------------------------------併發

Go與其餘語言不同,它從語言層面就已經支持併發,不須要咱們依託Thread庫新建線程。Go中的channel機制使咱們不用過多考慮鎖和併發安全問題。channel提供了一種goroutine之間數據流傳輸的方式。函數

今天我想從一個常見的deadlock error開始,討論一下channel的特性。ui

若是運行如下程序:spa

var ch = make(chan int) func main() { ch <- 1 <-ch // 沒有這行代碼也會報一樣的錯誤 } 

terminal會報以下錯誤:.net

fatal error: all goroutines are asleep - deadlock!

回顧channel(信道)的概念,大體上來講,信道是goroutine之間相互溝通的管道,信道中數據的流通表明着goroutine之間內存的共享。宏觀上來說,信道有點像其餘語言中的隊列(queue),遵循先進先出的規則。線程

信道分爲無緩衝信道(即unbuffered channel)和有緩衝信道(buffered channel)。對於無緩衝的信道來講,咱們默認信道的發消息(send)和收消息(receive)都是阻塞(block)的。換句話來講,無緩衝的信道在收消息和發消息的時候,goroutine都處於掛起狀態。除非另外一端準備好,不然goroutine沒法繼續往下執行。code

上面的那段程序即是一個明顯的錯誤樣例。在main函數執行到ch <- 1的時候main(也是一個goroutine)便已掛起,而並無其餘goroutine負責接收消息,而下面一句 <-ch 永遠沒法執行,系統便自動判爲timeout返回error。這種全部線程或者進程都在等待資源釋放的狀況,咱們便把它稱之爲死鎖。blog

死鎖是一個很是有意思的話題,常見的死鎖大體分爲如下幾類:
i. 只在單一goroutine裏操做信道,例子如上。
ii. 串聯信道中間一環掛起,舉例以下:隊列

var ch1 chan int = make(chan int) var ch2 chan int = make(chan int) func say(s string) { fmt.Println(s) ch1 <- <- ch2 // ch1 等待 ch2流出的數據 } func main() { go say("hello") <- ch1 // 堵塞主線 } 

ch1等待ch2留出數據,然而ch2並無發出數據致使goroutine阻塞,解決方案是給ch2喂數據:

func feedCh2(ch chan int) { ch <- 2 } 

iii. 非緩衝信道不成對出現:

c, quit := make(chan int), make(chan int) go func() { c <- 1 // c通道的數據沒有被其餘goroutine讀取走,堵塞當前goroutine quit <- 0 // quit始終沒有辦法寫入數據 }() <- quit // quit 等待數據的寫 

固然,並不是全部不成對出現的非緩衝信道都會報錯:

func say(ch chan int) { ch <- 1 } func main() { ch := make(chan int) go say(ch) } 

有意思的是,雖然say函數掛起等待信道接收消息,可是main goroutine並無被阻塞,在main函數返回後程序依然能夠自動終止。

關於緩衝信道將會在以後的文章中介紹,若有意見還請指教。

Reference: http://blog.csdn.net/kjfcpua/article/details/18265441

做者:Solonk8 連接:https://www.jianshu.com/p/147bd63801b6 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。
相關文章
相關標籤/搜索