Golang - 教學 tutorial

GoLang - select 監聽多 channel 及設定逾時策略

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

前面我們提到單一個 cannel 的情況如何發送或接收值,在這裡要針對多個 channel 的監聽方式進行說明。

Go 語言的 select 就可以用來監聽多個 channel,select 預設是阻塞的形式,當監聽的 channel 有接收或發送時,才會執行對應的 case

select 監聽多 channel

在這裡,建立兩個 channel ,並且分別執行,在透過 select 監聽

package main

import (
  "fmt"
  "time"
)

func writeData(intChan chan int) {
  for i := 1; i <= 3; i++ {
    intChan <- i
    fmt.Printf("writeData = %v\n", i)
  }
}

func writeData2(secChan chan int) {
  for i := 1; i <= 3; i++ {
    secChan <- i
    fmt.Printf("writeSecData ==> %v\n", i)
  }
}
func main() {
  intChan := make(chan int, 3)
  secChan := make(chan int, 10)
  go writeData(intChan)
  go writeData2(secChan)
  for {
    select {
    case v := <-intChan:
      fmt.Printf("ReadData = %v\n", v)
      time.Sleep(time.Second)
    case n := <-secChan:
      fmt.Printf("ReadSecData ==> %v\n", n)
      time.Sleep(time.Second)
    default:
      fmt.Printf("Nothine input\n")
    }

  }
  fmt.Println("end process")

}

接下來,查看輸出會發現問題,當監聽的 channel 讀寫完畢,仍繼續在執行 select 監聽

輸出

writeData = 1
writeData = 2
writeData = 3
ReadData = 1
writeSecData ==> 1
writeSecData ==> 2
writeSecData ==> 3
ReadSecData ==> 1
ReadData = 2
ReadSecData ==> 2
ReadSecData ==> 3
ReadData = 3
Nothine input
Nothine input
....

如何設定逾時,避免 blocking

有時,goroutine 執行過程出現阻塞,可能會造成整個系統也進入阻塞狀況。

避免 blocking 的方式,可以透過 select 增加限時設定,超過時間就觸發逾時程序:

package main

import (
  "fmt"
  "time"
)

func writeData(intChan chan int) {
  for i := 1; i <= 3; i++ {
    intChan <- i
    fmt.Printf("writeData = %v\n", i)
  }
  close(intChan)
}

func writeData2(secChan chan int) {
  for i := 1; i <= 3; i++ {
    secChan <- i
    fmt.Printf("writeSecData ==> %v\n", i)
  }

  close(secChan)
}

func main() {
  intChan := make(chan int, 3)
  secChan := make(chan int, 10)
  go writeData(intChan)
  go writeData2(secChan)

  //設定逾時
  timeout := time.After(5 * time.Second)
Loop:
  for {
    select {
    case v, ok := <-intChan:
      if ok {
        fmt.Printf("ReadData = %v\n", v)
      }
    case n, ok := <-secChan:
      if ok {
        fmt.Printf("ReadSecData ==> %v\n", n)
      }
    case <-timeout:
      fmt.Println("timeout")
      break Loop
    default:
      //在這裡可決定是否結束
      fmt.Printf("Nothine input\n")
    }
  }
  fmt.Println("End")

}

輸出:

ReadData = 1
writeSecData ==> 1
writeSecData ==> 2
writeSecData ==> 3
ReadSecData ==> 1
ReadSecData ==> 2
ReadSecData ==> 3
writeData = 1
writeData = 2
writeData = 3
ReadData = 2
ReadData = 3
timeout
End