Golang - 教學 tutorial

GoLang - catch panic 避免主系統錯誤

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

在 Go 語言並沒有 try catch 的機制,在系統運行過程,若發生 panic 則會導致整個系統崩壞。

如何解決 panic 導致崩潰

在這裡舉前面監聽多 channel 的例子,在 goroutine 運行過程,若發生錯誤,同樣會連帶造成主程式錯誤,

因此在運行時,可以透過 recover 來捕獲 panic 的行為,例如:

func writeData(intChan chan int) {

  //catch panic
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("WriteData error happened:", err)
    }
  }()

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

接著,在這部分我們刻意在過程中引發 panic

func writeData2(secChan chan int) {
  //catch panic
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("WriteData2 error happened:", err)
    }
  }()
  for i := 1; i <= 3; i++ {
    secChan <- i
    fmt.Printf("writeSecData ==> %v\n", i)

    var testErrorMap map[int]string
    testErrorMap[0] = "error happened"

  }

  close(secChan)
}

透過捕捉 panic 可以讓錯誤發生時,不會導致系統崩壞,以下是輸出內容:

Nothine input
writeSecData ==> 1
ReadSecData ==> 1
ReadData = 1
Nothine input
Nothine input
Nothine input
WriteData2 error happened: assignment to entry in nil map
writeData = 1
writeData = 2
writeData = 3
ReadData = 2
ReadData = 3
timeout

最後,附上最終完整內容

package main

import (
  "fmt"
  "time"
)

func writeData(intChan chan int) {

  //catch panic
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("WriteData error happened:", err)
    }
  }()

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

func writeData2(secChan chan int) {
  //catch panic
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("WriteData2 error happened:", err)
    }
  }()
  for i := 1; i <= 3; i++ {
    secChan <- i
    fmt.Printf("writeSecData ==> %v\n", i)

    var testErrorMap map[int]string
    testErrorMap[0] = "error happened"

  }

  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")

}