在 go 語言裡要使用 goroutine 非常簡單,語法如下,不會像 C 語言使用 thread 有太多複雜的語法。(注意:goroutine 很像 thread 但實際上卻不是 thread,這裡不談,想看 goroutine 和 thread 的差異已經有很多討論)。
go func() {
doSomething()
}()
通常多個 goroutine 會互相溝通,達到正確執行的結果,剛開始學 gorotuine 時會寫出像以下的程式,然後什麼都沒印出來就結束了,這就是因為 main goroutine 和此 goroutine 沒有溝通的結果:在 go spec 指出,要是 main 執行完了是不會等其他 goroutine 的,也就是說在 main goroutine 執行的這段時間裡,根本來不及等到 scheduler 排程此匿名 goroutine 並且執行,就結束了。
從這可以觀察到:單單只有 goroutine 是不行的,我們需要有工具讓多個 goroutine 互相溝通,這工具其實就是 channel。
// Nothing will print
func main() {
go func() {
fmt.Println("Hello world")
}()
}
預期的寫法應該如下,其實就是多創造了一個 channel,並且兩個不同的 goroutine 做了以下事情
func main() {
done := make(chan string)
go func() {
fmt.Println("Hello world")
done <- "done"
}()
<- done
}
這裡只是先給出一個概念,知道需要 goroutine 需要 channel 才會運作的良好。沒看懂沒關係,之後就看懂了。
channel 是用來給 goroutine 溝通的。 goroutine 可以對 channel 做兩個操作 send 和 receive。
以下就是建立一個 channel 的語法。 string 型態代表,此 channel 所 send 和 receive 的是一個 string。
ch := make(chan string)
send 操作,將某個值放入 channel
ch <- "hello"
receive 操作,使用某個變數接收 channel 裏的值 (line 1),也可以接收到之後把這個值丟掉 (line 2)
myVar := <- ch
<- ch
接下來我們將會談到,channel 的 block 機制
以下我將會使用幾個名詞:傳送者就是只使用 send 的人,接收者是指使用 receive 操作的人
我們可以把 unbuffered channel 想像成一個掛號信的傳送,信件一定要傳輸到收信人手上,我們分別用傳送者跟接收者的角度來看:
傳送者:目的把信件送到接收者手上,等到接收者出現我才去做其他事情
接收者:目的在從傳送者手上收到信件,等到接收者出現我才去做其他事情
我們剛剛所講的語法其實就是 Unbuffered channel,再看一次:
ch := make(chan string)
把 Buffered Channel 想像成一種特殊的平信,信件一定要塞到信箱裡,我們分別用傳送者跟接收者的角度來看:
傳送者:目的把信件送到信箱裡,如果信箱滿了,那就等,等到信有位置了再把信塞進去
接收者:目的在把信件從信箱拿出來,如果信箱空了,那就等,等到有信了再從信箱拿出來
以下就是 buffer 大小為五個 string 的 channel,如果在 channel 沒有人接收的情況下,在 send 一個 string 則會 block
ch := make(chan string, 5)
我們可以透過 close
語法關閉 channel
ch := make(chan string)
close(ch)
注意:
func main() {
ch := make(chan int, 2)
go func() {
ch <- 1
ch <- 2
close(ch)
}()
fmt.Println(<-ch) // print 1
fmt.Println(<-ch) // print 2
fmt.Println(<-ch) // print 0
}
Reference: https://golang.org/ref/spec#Close