Golang - 教學 tutorial

GoLang - Channels

在這幾篇,會以 Go 語言的入門基礎進行逐步說明,本篇針對 Channel 進行說明

前面我們提到關於 gorotuine ,雖然 goroutine 提供了共用記憶體資料的功能,但是在 goroutine 之間還是會透過 channel 來進行通訊。

Go 語言的 channel 具有以下特性:

  • 具有線程安全 不需加鎖,多線程操作同一個 channel 不會造成資源競爭
  • 只能存放指定型別的數據
  • 可用於 goroutine 之間的溝通渠道
  • 先進先出(FIFO)
  • 包括阻塞(blocking) 及非阻塞(unblocking)
  • channel 放滿了就無法在放入數據
  • 若從 channel 取出數據,就可在繼續放入
  • 沒有使用 goroutine 情況,數據讀取完畢若在讀取,會報出dead lock

接下來,我們會針對 channel 以及他的 buffered/unbuffered 特性來進行說明。

UnBuffered Channel:

go 語言的 channel 預設就是屬於 unbuffered channel,因此,channel在接收跟發送資料過程都是阻塞。

簡單來說,當 channel 接收到值,就會停止在讀取數據,直到 channel 數據被讀取出去,才能再次接收數據。

從另一方面,當 channel 還未有值就被讀取時,會先呈現 blocking 狀態,直到有值傳入 channel 中,才會順利完成讀取動作。

package main

import (
  "fmt"
)

func addData(ch chan int) {
  ch <- 1
}

func main() {

  c := make(chan int)

  go addData(c)

  fmt.Println(<-c)
}
//output 1

使用 range loop 來操作

在這裡,我們透過 interface{} 來帶入任意型別數值,並且在生產端執行 close() 將 channel 關閉,被關閉的 channel 就無法再發送資料。這時,就可以透過 range 來操作 channel,將數據取出。

func addData(ch chan interface{}) {
  for i := 0; i < 10; i++ {
    ch <- i
  }
  close(ch)
}

func main() {

  c := make(chan interface{})

  go addData(c)

  for v := ruang c {
    fmt.Println(v)
  }
}

//output 0 1 2 3 4 5 6 7 8 9

Buffered Channels:

Go 的 Buffered channels 意思是將 channel 設定緩衝值,例如設定為 5 表示 channel 可以緩衝儲存 5 個元素,指定方式如下:

ch = make(chan type, num)   //num 就表示為緩衝值

例如,在這裡我們指定緩衝為 2 ,則可以在 channels 陸續塞入2 個緩衝元素。

package main

import (
  "fmt"
)

func main() {

  ch := make(chan int, 2)

  ch <- 1
  ch <- 2

  fmt.Println(<-ch)
  fmt.Println(<-ch)

}

若將緩衝修改為 1,則會出現錯誤訊息:

fatal error: all goroutines are asleep - deadlock