Go - Channels

Go - Channels

September 23, 2024

Channel

Channel 是個 typed 的管道,go 程式可以透過 channel operator <- 去接受或發送某個值。透過 channel 可以同步不同的 Goroutine。

箭頭的方向就代表了資料的傳輸方向:

ch := make(chan int) // create a channel for int
v := 100

go func() {
	fmt.Println("Sending v to channel")
	ch <- v // Send value to the channel
}()

recv := <- ch // Get value from the channel
fmt.Printf("recv: %v", recv)

預設情況下,接受與發送都會 block 住,直到另一端也發送或接受,像是以下的程式會停在 ch <- v,因為程式還在等人發送值到 channel 中,會出現 deadlock 錯誤 fatal error: all goroutines are asleep - deadlock!

ch := make(chan int) // create a channel for int
v := 100

fmt.Println("Sending v to channel")
ch <- v // The code will stop here and wait for the value...

recv := <- ch
fmt.Printf("recv: %v", recv)

channel 的 block 行為是可以被控制的,channel 可以宣告成 buffered channel,一個 buffered channel 裡的值可以保存起來,接受端只要拿的到值就可以繼續,發送端只要發送值進去後 channel 沒有超過容量也可以繼續。

buffered channel 的容量透過 make() 的第二個參數指定:

ch := make(chan int, 1)

將上面會造成 deadlock 的 channel 改成 buffered channel (size = 1) ,就可以正常執行了:

ch := make(chan int, 1) // create a buffered channel for int
v := 100

fmt.Println("Sending v to channel")
ch <- v

recv := <- ch
fmt.Printf("recv: %v", recv)

Close the channel

  • Sender 可以透過 close(channel) 通知 receiver 之後不會傳東西到這個 channel 中了。
  • Receiver 可以透過 v, ok := <- chokfalse 得知 channel 已被關閉,也可以透過 for i := range c 接受值,直到 channel 被關閉。

Select

  • select 可以從多個 case 中執行符合的操作。
  • 如果同時有多個條件都符合,隨機選一個。
  • select (在沒有定義 default 時),會 block 並等待某個 case 並執行對應的操作:
  • select 加上 default 後就不會 block,因為當其他 channel 都還沒好時,會進到 default 中,見官方範例參考:
	tick := time.Tick(100 * time.Millisecond)
	boom := time.After(500 * time.Millisecond)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("BOOM!")
			return
		default:
			fmt.Println("    .")
			time.Sleep(50 * time.Millisecond)
		}
	}

Refs