[Go] Go言語による並行処理 4章メモ1
Go言語による並行処理の第4章をまとめていく。 www.amazon.co.jp
4.1 拘束
並行プロセスを安全にする方法として、
- データをイミュータブルにする(作成後に変更できないようにする)
- データを拘束によって保護する
などが挙げられる。 拘束について、
- アドホック拘束: 規約によって拘束を達成する
- レキシカル拘束: レキシカルスコープを使って適切なデータと並行処理のプリミティブだけを複数の並行処理プロセスで使えるように公開する
の2つがある。
レキシカル拘束
3章でみた、チャネルの読み書きの必要な権限だけを公開するというのもその1つ
次の例では、printData
はdata
スライスには直接アクセスできず、引数として渡さねばならない。
また、printDataの中ではdata
スライスの一部しか見ることができない。
printData := func(wg *sync.WaitGroup, data []byte) { defer wg.Done() var buff bytes.Buffer for _, b := range data { fmt.Fprintf(&buff, "%c", c) } fmt.Println(buff.String()) } var wg sync.WaitGroup wg.Add(2) data := []byte("golang") go printData(&wg, data[:3]) go printData(&wg, data[3:]) wg.Wait()
for-selectループ
for-selectループを用いるパターンを挙げる。
- チャネルから繰り返しの変数を送出する
for _, s := range []string{"a", "b", "c"} { select { case <- done: return case stringStream <- s: } }
- 停止シグナルを待つ無限ループ
for { select { case <-done: return default: } // 割り込みできない処理 }
または、
for { select { case <-done: return default: // 割り込みできない処理 } }
4.3 ゴルーチンリークを避ける
ゴルーチンが終了するのは、
- 処理を完了する場合
- 回復不可能なエラーで処理が続けられない場合
- 停止するよう命令された場合
あり、3のようにゴルーチンを終了するには、done
という読み込み専用チャネルを使用する。
ゴルーチンがゴルーチンを生成したのなら、その生成したゴルーチンを停止できるようにするべきである。
doWork := func(done <-chan interface{}, strings <-chan string) <- chan interface{} { terminated := make(chan interface{}) go func() { defer fmt.Println("doWork exited") defer close(terminated) for { select { case s := <- strings: // 処理 fmt.Println(s) case <- done: return } } }() return terminated } done := make(chan interface{}) terminated := doWork(done, nil) go func() { // 1秒後に操作をキャンセル time.Sleep(1 * time.Second) fmt.Println("Canceling doWork goroutine...") close(done) } <-terminated fmt.Println("done")
4.4 orチャネル
1つ以上のチャネルを1つのチャネルにまとめ、どれか1つが閉じられたら全部のチャネルを閉じるようにする。 再帰をつかったorチャネルで実現できる(実装略)
4.5 エラーハンドリング
取得される結果とエラーを対にする。
http.Get(url)
をするプログラムであれば、
type Result struct { Error error Response *http.Response }
を作り、2つをまとめる。