GoLang - Goroutine (上) 基本原理與結構

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

goroutine 與 thread 差異

在 Go 語言中,goroutine 非常類似於執行緒,他運行的方式是透過多工的方式並行,本質上一個 goroutine 是單執行緒,而 thread 屬多執行緒,每個執行緒之間屬於同步機制,同時運行是以經典搶佔多工方式運作。

簡單來說,10 幾個 goroutine 運作的底層只有 5 個 thread。

在 goroutine 與其他語言的 coroutine 相似,在運行過程只需要少量的記憶體用量(4k~5k),擁有自己的上下文,以及暫存這些上下文,可以在不同時間點分段執行任務。

在 Go 語言另外針對 goroutine 實作了 goroutine 之間的記憶體共用機制,並且設計了 channel 讓 goroutine 之間可以進行資料溝通。

但是,實際設計平行處理時,要謹記!不要透過共用記憶體機制,而是要透過 channel 來進行通訊

在 Go 語言包含了 runtime 這個 package, runtime 的 operations 的方法中,就包含控制 goroutines 的 functions。

只要透過 go

CVT2HUGO: 關鍵字就可以啟動 ```goroutine```查看以下範例
package main

import (
  "fmt"
  "runtime"
)

func showData(txt string) {
    fmt.Println("==>", txt)
}

func main() {
  go showData("hi")
  showData("all")
}

Environment variables

這裡先介紹一些 runtime 的環境變數,針對 Environment variables 後續再進行詳細的介紹:

GOGC: 可以用來設定垃圾回收(garbage collection)的百分比,預設為 100,可以設定為 off 表示關閉回收機制。

SetGCPercent( ): 可以透過這個函數來設定 GOGC 的值。

GODEBUG: 控制 runtime 的 debugging variable ,可以透過 name=value, name=value, … 的方式設定,主要的 name 包含有以下幾種,在 net, net/http, 與 crypto/tls 也有參考到 debugging variables,詳細的用法建議可參考這裡

allocfreetrace
clobberfree
cgocheck
efence
gccheckmark
gcpacertrace
gcshrinkstackoff
gcstoptheworld
gctrace
madvdontneed
memprofilerate
invalidptr
sbrk
scavenge
scavtrace
scheddetail
schedtrace
tracebackancestors
asyncpreemptoff

GOMAXPROCS(n): 可以設定可執行的CPU核心,限制同時在 user-level 的 threads 的數量

GORACE( ) : 可以用來控制 race detector ,在兩個goroutines 訪問同一個 variable 產生 data race 時的作法,細節可參考這裡

GOTRACEBACK( ): 當 go 運作失敗時,可以控制 output 的內容

Goexit( ): 可以退出目前的 goroutine ,退出前會觸發 defer

Goshed( ): 可以將目前 gorotuine 使用的CPU時間切片讓給其他 goroutine,並且不會暫停,等有空閒的CPU切片時,再接續進行

NumCPU( ): 可以取得目前CPU的核心數

NumGoroutine( ): 可以回傳目前正在運行的 goroutines 數量

下方舉個例子, 先執行一個正常呼叫函數的流程:

package main

import (
  "fmt"
  "runtime"
)

func showData(txt string) {
  for i := 0; i < 5; i++ {
    runtime.Gosched()
    fmt.Println("RealOutput==>", txt)
  }
}

func main() {
  fmt.Println("stage 1")
  showData("hi")
  fmt.Println("stage 2")
  showData("all")
  fmt.Println("stage 3")
}

output

stage 1
RealOutput==> hi
RealOutput==> hi
RealOutput==> hi
RealOutput==> hi
RealOutput==> hi
stage 2
RealOutput==> all
RealOutput==> all
RealOutput==> all
RealOutput==> all
RealOutput==> all
stage 3

接著,將第一個呼叫函示的方式用 goroutines 方式

package main

import (
  "fmt"
  "runtime"
)

func showData(txt string) {
  for i := 0; i < 5; i++ {
    runtime.Gosched()
    fmt.Println("RealOutput==>", txt)
  }
}

func main() {
  fmt.Println("stage 1")
  go showData("hi")
  fmt.Println("stage 2")
  showData("all")
  fmt.Println("stage 3")
}

output

stage 1
stage 2
RealOutput==> all
RealOutput==> all
RealOutput==> all
RealOutput==> all
RealOutput==> all
stage 3
RealOutput==> hi
RealOutput==> hi
RealOutput==> hi