Golang 學習筆記三
goroutine
A goroutine is a light weight thread managed by the Go runtime.
go f(x, y, z)
`f, x, y, z` 的計算會發生在當前 goroutine,但執行在另一個新的 goroutine
goroutine 運行在同一個地址空間,所以訪問共享內存時需要同步
channel
Go 哲學不要使用共享內存通信,而是通過 channel 通信來達到共享內存的目的
channel 是用於 goroutine 之間的通信
通道是一個通信管道,可以通過通道操作符 `<-` 來接收,發送消息(PS: 估計類似與進程管道進行通信)
表現同時也像隊列,先進先出。普通 channel 可以用來做同步控制(synchronization),因為 channel 的一方必須等另一方準備完成才能進行讀寫,比如:
Sender 把一個數據放進 channel,Reader 還未進行讀,此時 Sender 再次把數據放進 channel 就會阻塞 Sender,知道 Reader 把數據從 channel 讀取出來
一個 goroutine 可以寫和讀 channel,但是不能夠讀自己寫的數據,讀自己寫的數據將會發生阻塞,如果所有 goroutine 都 asleep 那麼 go runtime 認為出現了死鎖
ch <- v // 發送 v 到 channel chv := <-ch // 從 channel ch 接收一個值,並且賦予 v
數據流動方向就是箭頭方向,`<-`
創建 channel
ch := make(chan int)
默認情況下,直到對方準備好,才開始發送和接收消息。這提供了 goroutine 之間的同步機制,而不是採用特殊的鎖或條件變數
buffered channel
channel 可以是有緩存的,提高緩存的長度用來初始化 channel
ch := make(chan int, 2)
向滿的 buffered channel 中輸入數據,或者是向空 buffered channel 中讀取數據,會發生錯誤
Range and Close
發送者可以 `close` 關閉 channel,表明沒有再多的元素被發送;
接受者可以測試一個 channel 是否已經關閉 `v, ok := <- ch`
只有發送者才能夠關閉 channel,向已經關閉了的 channel 發送數據會引發 `panic`
`ok == false` 如果 channel 中沒有更多元素,channel 已經被關閉
使用 for 循環讀取 channel 中的數據,直到 channel 被關閉
for i := range ch
不像文件,關閉 channel 並不是必須的。只有當需要通知接受者沒有更多的元素時,才需要主動關閉 channel,例如通知接受者停止 `for i := range ch` 循環
讀取 buffered channel 目前元素個數 `length` 和可以容納的最多元素個數 `capability`
len(ch)cap(ch)
從 channel 中不斷發送和讀取 fibonacci 數列
func fibonacci(n int, ch chan int) {x, y := 0, 1for i := 0; i < n; i++ { ch <- yx, y = y, x + y }close(ch)}func main() {ch := make(chan int, 10)go fibonacci(cap(ch), ch)// 不斷地讀取元素,直到 channel closedfor i := range ch { fmt.Println(i) }}
select
select 從多個 channel 中隨機選取一個可讀或者可寫的 channel,執行該 case。
func fibonacci(c, quit chan int) {x, y := 0, 1for {select {case c <- y:x, y = y, x+y// 從 quit 通道中讀取元素,但是沒有發生賦值case <-quit: fmt.Println("quit")return } }}func main() {c := make(chan int)quit := make(chan int)go func() {// 從 c 中讀取 10 個元素,然後向 quit 中發送信號,讓程序退出for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }()fibonacci(c, quit)}
time.After
用於控制超時 select
select {case v := <-ch:doSomething()// time.After 可以保證一定時間後 channel 即可通信case <-time.After(1 * time.Second):timeout()}
sync.Mutex
sync.Mutex 用於同步,比如上鎖等操作
mux.Lock() // 上鎖defer mux.Unlock() // 解鎖
goroutine 不是 thread,每一個 goroutine 都擁有一個自己的調用棧。goroutine 開銷很低,可以同時開啟上千個 goroutine。
有可能一個 thread 中有上千個 goroutine。goroutine 多路動態復用 thread,也就是說真正執行計算的還是 thread,但是一個 thread 可能利用非同步等技術來 concurrent 並發多個 goroutine 執行,並且減少切換 thread 上下文的成本。
推薦閱讀: