Don't communicate by sharing memory; share memory by communicating.
不同於過去的 muliti thread 的程式開發, 常使用共用變數去做資訊傳遞 (communicate by sharing memory).
goroutine 彼此的溝通方式是使用 channel (share memory by communicating). 這樣的方式會讓整個流程在表現上變得清楚.
channel 的通訊, 需要一個 sender 和一個 receiver, 當 sender 和 receiver 都 ready 時候, 訊息才會成功的傳遞.
import "fmt"
func main(){
s := mack(chan string) //宣告一個 channel 變數
s <- "hello" //寫入 channel (sender)
val <- s //讀取 channel (receiver)
fmt.Println(val)
}
上例說明了如何宣告 channel 變數, 寫入 channel, 讀取 channel.
但是 code 無法跑, compiler 會顯示 dead lock.
因為執行到 s <- "hello" 這步的時候, sender 就會進入 ready 狀態.
然後就停住了, 也就是他不會執行到 val := <- s .
這時我們建立一個 goroutine 去跑 s <- "hello"
import "fmt"
func main(){
s := make(chan string) //宣告一個 channel 變數
go func(){
s <- "hello" //寫入 channel (sender)
}()
val := <- s //讀取 channel (receiver)
fmt.Println(val)
}
執行順序就是
1. 建立一個 goroutine //此時 s <- "hello" 還沒執行
2. 執行 val := <- s //s 空的, receiver ready (停住)
3. 執行 s <- "hello" //sender 將訊息寫入 s, sender ready
4. val := <- s //成功讀取 s, (因為 receiver, sender 都 ready)
5. fmt.Println(val)
goroutine 不見得會比較慢執行. 不過重點不在這, 就不多描述了.
所以要看執行的順序就要注意這原則 "當 sender 和 receiver 都 ready 時候, 訊息才會成功的傳遞. "
再舉一個例子, 請問順序式如何?
import "fmt"
func main(){
s := make(chan string)
go func() {
for i := 0; i < 3; i++ {
fmt.Println("sender hello",i)
s <- fmt.Sprintf("receiver hello %d", i)
}
}()
for i := 0; i < 3; i++ {
val := <-s
fmt.Println(val)
}
}
結果
sender hello 0
sender hello 1
receiver hello 0
receiver hello 1
sender hello 2
receiver hello 2
有對嗎?程式碼順序是這樣
1. 建立一個 goroutine
2. 執行 val := <- s //s 空的, receiver ready (停住)
[sender hello 0]
3. 執行 s <- fmt.Sprintf.. //sender 將訊息寫入 s, 傳訊成功
[sender hello 1]
4. 執行 s <- fmt.Sprintf.. //s 有值, sender ready(停住)
5. val := <-s //讀到 s 的值
[receiver hello 0]
6. val := <-s //s 空的, receiver ready, 傳訊成功
[receiver hello 1]
7. val := <-s //s 空的, receiver ready (停住)
[sender hello 2]
8. 執行 s <- fmt.Sprintf.. //sender 將訊息寫入 s, 傳訊成功
9. val := <-s //讀到 s 的值
[sender hello 2]
另外我們可以修改 channel 的大小為 2
import "fmt"
func main(){
s := make(chan string,2)
go func() {
for i := 0; i < 3; i++ {
fmt.Println("sender hello",i)
s <- fmt.Sprintf("receiver hello %d", i)
}
}()
for i := 0; i < 3; i++ {
val := <-s
fmt.Println(val)
}
}
結果
sender hello 0
sender hello 1
sender hello 2
receiver hello 0
receiver hello 1
receiver hello 2
http://guzalexander.com/2013/12/06/golang-channels-tutorial.html