guest@blog.cmj.tw: ~/posts $

Go Channel


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)
}