今天來講Channel是Go語言中的一個重要概念,它是用來在不同的goroutine之間傳遞數據的一種方式(類似於Unix中的管道),這使得Go語言在處理併發任務時非常優雅和高效
在Go語言中,Channel是一個引用類型,需要使用make()函數來初始化。否則沒有初始化的Channel是nil。Channel通道中的數據是按照先進先出(FIFO)的原則進行傳遞的。
當對Channel進行資料讀取或寫入時,會阻塞當前的goroutine,然後把執行環境交給另一個 goroutine,直到 channel 有進有出後才會回到該 goroutine 作用環境。以確保資料的正確性。且因此我們可以不用額外的同步機制來保證資料的正確性。
// read only channel
var chronly <-chan int
// write only channel
var chwrite chan<- int
// read and write channel
var ch chan int
// make a channel
ch := make(chan int)
chBuffered := make(chan int, 10) // buffered channel with capacity 10
可以注意到,對於只能寫入或讀取的Channel宣告,我們可以透過箭頭指向哪裡來分辨是寫入還是讀取的Channel,從chan指到變數表示這是一個唯讀Channel,從類別指到chan表示這是一個唯寫Channel
而對於Buffer Channel(在make時有指定容量),當Channel滿了時,寫入操作會被阻塞,直到有空間可以寫入,當Channel為空時,讀取操作會被阻塞,直到有數據可以讀取,因此可以用來確保goroutine完成後再繼續執行
如果你在同一個goroutine中寫入超過Channel的容量,會導致deadlock
我們一樣可以透過箭頭來指定寫入或讀取的Channel,簡單來說, 從Channel指出來的箭頭表示讀取,指向Channel的箭頭表示寫入
ch := make(chan int)
go func() {
ch <- 1 // write to channel
}()
go func() {
fmt.Println(<-ch) // read from channel
}()
func readFromChannel(ch <-chan int) {
fmt.Println(<-ch) // read from channel
}
如果確定沒有要繼續寫入資料到Channel,可以使用close(ch)
函數來關閉Channel,這樣可以避免deadlock。關閉後可以繼續讀取Channel中的資料,但是無法再寫入資料到Channel
ch := make(chan int)
go func() {
ch <- 1 // write to channel
close(ch)
}()
可以使用v, ok := <-ch
來檢查Channel是否已經關閉,如果Channel已經關閉時, ok會返回false
ch := make(chan int)
go func() {
ch <- 1 // write to channel
close(ch)
}()
for {
v, ok := <-ch
if !ok {
break
}
fmt.Println(v)
}
你甚至可以使用range
來輸出Channel中的所有資料,因為在range中的Channel會自動檢查Channel是否已經關閉,並且在Channel關閉後自動退出迴圈
ch := make(chan int)
go func() {
ch <- 1 // write to channel
ch <- 2 // write to channel
close(ch)
}()
for v := range ch {
fmt.Println(v)
}
那麼今天的文章就到這告一段落,如果我的文章有任何地方有錯誤請在留言區反應
明天將會介紹Go語言的select語句及其在非同步中的應用,你可以把它想像成switch case的非同步版本