iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 28
0

Channel可以當Goroutine間溝通的管道,使用channel的方法很簡單
先宣告一個channel的變數,基本上channel傳遞資料的型態沒有限制,要傳遞interface也是可以的

channel的操作


channel的寫入與讀取是用<-這個符號來做操作

//把值寫入channel
ch <- "second"
//從channel中讀資料出來
receiver := <-ch

channel的型類


  • chan string :可以讀取與寫入strin類型的資料
  • chan<- string :只能傳送strin類型的資料
  • <-chan string :只能接收strin類型的資料

以下面的範例在說明怎麼把chan當參數傳遞與channel在Goroutine中當資料傳遞的角色

func main() {
	ch := make(chan string)
	go testChannel(ch)
	go channelTest(ch)
	for {

	}
}
func testChannel(ch chan<- string) {
	fmt.Println("sender to channel")
	ch <- "mdfk2020"
}
func channelTest(ch <-chan string) {
	receiver := <-ch
	fmt.Println("receiver ", receiver)
}

執行結果

sender to channel
receiver  mdfk2020

unbuffered 與 buffered channel

二者的差異性

  • unbuffered channel:需要讀跟寫都要做完才會繼續走下去,如果只滿足一邊就會阻塞在那邊,如果只有單一線程去做塞資料到channel時,會導致線程卡住不會做下去,因為沒有線程去做讀資料出來,然後就deadlock啦
func main() {
	ch := make(chan string)
	ch <- "first"
	receiver := <-ch
	fmt.Println(receiver)
    //fatal error: all goroutines are asleep - deadlock!
}

解決方法

func main() {
	ch := make(chan string)
	go func() {
		ch <- "first"
	}()
	receiver := <-ch
	fmt.Println(receiver)
}
  • buffered channel:跟unbuffered channel相反,只要寫入就會繼續向下做,只是小心別塞超過大小
func main() {
	ch := make(chan string, 1)
	ch <- "first"
	receiver := <-ch
	fmt.Println(receiver)
    //妥妥的~~
}

塞超過設定值,直接噴掉啦

func main() {
	ch := make(chan string, 1)
	ch <- "first"
	ch <- "second"
	receiver := <-ch
	fmt.Println(receiver)
    //fatal error: all goroutines are asleep - deadlock!
}

channel的close

主要是關閉channel不被寫入,但是還是可以讀取喔

func main() {
	ch := make(chan string)
	go testChannel(ch)
    go channelTest(ch)
	for {
	}
}
func testChannel(ch chan<- string) {
	fmt.Println("sender to channel")
	ch <- "mdfk2020"
    //把channel關閉
	close(ch)
}
func channelTest(ch <-chan string) {
	receiver := <-ch
	fmt.Println("receiver ", receiver)
}

執行結果,依然讀取的到channel寫入值

sender to channel
receiver  mdfk2020

如果我把channel關閉後再寫入會發生什麼事情

func main() {
	ch := make(chan string)
	go testChannel(ch, "mdfk2020")
	go channelTest(ch)
	time.Sleep(2 * time.Second)
	go testChannel(ch, "GGININDER")
	go channelTest(ch)
	for {
	}
}
func testChannel(ch chan<- string, source string) {
	fmt.Println("sender to channel")
	ch <- source
	close(ch)
}
func channelTest(ch <-chan string) {
	receiver := <-ch
	fmt.Println("receiver ", receiver)
}

執行結果,第一組正常,第二組的讀寫因為遇到channel close的關閉導致系統噴掉,

sender to channel
receiver  mdfk2020
receiver  
sender to channel
panic: send on closed channel

close channel的角色不可以是讀取者/接收者

channle的close有一個原則,receiver不可以close channel,如果receiver進行close會發生什麼事情呢?
會編譯不過XD,這種錯誤比runtime才噴掉好

invalid argument: ch (variable of type <-chan string) must not be a receive-only channel

以上這種情形就是

The Channel Closing Principle

在使用Go channel的時候,一個適用的原則是不要從接收端關閉channel,也不要關閉有多個並發發送者的channel。換句話說,如果sender(發送者)只是唯一的sender或者是channel最後一個活躍的發送者,那麼你應該在發送者的goroutine關閉頻道,從而通知receiver(s)(接收者們)已經沒有值可以讀了。維持這條原則將保證永遠不會發生向一個已經關閉的channel發送值或者關閉一個已經關閉的channel。
引用The Channel Closing Principle

如果想用channel的特性去卡住Goroutine的話,其實用waitGroup或是errgroup的Wait()也可以做到,
後面的文章會拿channel的特性去做graceful down的動作


上一篇
[DAY27]Golang最強大的特性:Goroutine -2
下一篇
[DAY29]Golang Graceful Shutdown優雅的關機(?)
系列文
欸你這週GO了嘛30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言