communication model
最近在弄一個新的 side project 順手研究一下 Go 的 channel model,發現使用上有點不順手,所以順手紀錄一下各種使用情境: 都固定使用一個 Job 的 struct 來作為節點 (node),並當 channel 當作是邊界 (edge) 來塑造一個有向圖 ( Directed Graph )
// the pseudo job node and the channel is the edge of the directed graph
type Job struct {
Name string
Chan chan string
}
List
一個最簡單的 model 就是串列 (list) 形式,也就是所有 Job 透過串列傳遞資料:
func List(jobs ...Job) {
wg := sync.WaitGroup{}
wg.Add(len(jobs)-1)
for idx, _ := range jobs {
if idx > 0 {
// last node, receive the data from previous node
go func(idx int) {
// received on the current node
data := <-jobs[idx].Chan
wg.Done()
}(idx)
}
if idx < len(jobs)-1 {
go func(idx int) {
// first node, send data to the next node
jobs[idx+1].Chan <- jobs[idx].Name
}(idx)
}
}
// block until all goroutine finished
wg.Wait()
}
在設計上需要注意的部分有:
- 需要使用 sync.WaitGroup 避免 main thread 直接結束
- 使用 goroutine 需要透過參數帶入,如果使用 global variable 則有機會出現 R.C. 狀況
而第二個狀況可以用以下的程式碼當作範例:每一次執行 x 都會被不同的 goroutine 所修改且最後的值也無法估計。
package main
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{}
wg.Add(4)
x := 1
for _, in := range []int{1, 2, 3, 4} {
go func(in int) {
fmt.Printf("%d -> %d\n", x, in)
x = in
wg.Done()
}(in)
}
wg.Wait()
fmt.Println(x)
}