KDOC 474: 『Go言語による並行処理』

この文書のステータス

  • 作成
    • <署名>
  • レビュー
    • <署名>

概要

『Go言語による並行処理』は、並行処理を解説する本である。

メモ

  • 並列性はランタイムの性質であって、コードの性質ではない(p24)
    • 並列で走ってほしいと考えて並行なコードを書いている
  • コードの結果を別のコードに共有したい場合、これはデータの所有権を移動していることになる。並行プログラムを安全にするには、一度に1つの並行処理のコンテキストのみがデータの所有権を持つようにする(p33)
  • 構造体の内部の状態を保護している場合はメモリアクセス同期を使うべきで、チャネルを使うべきではない(p34)
  • メモリアクセスするべきか、チャネルを使うべきかの判断基準がある
  • ゴルーチンは一時停止や再エントリーのポイントを定義していない。Goのランタイムはゴルーチンの実行時の振る舞いを観察して自動的に一時停止したり再開する(p38)
  • Goがゴルーチンを管理する機構はM:Nスケジューラという実装になっている。M個のグリーンスレッドをN個のOSスレッドに対応させる。ゴルーチンはグリーンスレッドにスケジュールされる(p39)
  • ゴルーチンは未来の任意のタイミングにスケジュールされる。ループ内でゴルーチンを発行していたら、順番に実行される保証はない
  • WaitGroupの使い方。
    • Add カウンタを増やす – 監視対象のゴルーチンの外で行う。いつゴルーチンが実行されるかはわからない。競合する可能性があるから
    • Done カウンタを減らす
    • Wait ゼロになるまでメインゴルーチンをブロック
  • Sync, Cond, WaitGroup, Once, Pool
  • オブジェクトプールデザインパターンはオブジェクトを要求するがインスタンス化のあとすぐにオブジェクトを捨てる並行処理のプロセスがある場合あるいはオブジェクトの生成がメモリに悪影響を与える場合に最適である(p64)
  • 一方向チャネルを宣言するには <- 演算子を使う(p66)
    • 読み込み専用チャネル: chan<-
    • 書き込み専用チャネル: <-chan
  • Goは双方向チャネルを必要に応じて暗黙的に一方向チャネルに変換してくれる(p66)
  • チャネルをrange処理すると、チャネルが閉じたときに自動的にループを終了する(p69)
  • チャネルを閉じるとチャネルの読み込みブロックが解除される。閉じたチャネルは無限に読み込める(p70)
  • チャネルが満杯のときはチャネルへの書き込みによってブロックし、チャネルが空のときはチャネルからの読み込みによってブロックする(p71)
  • nilチャネルの扱い。nilチャネルからの読み込み、書き込み、close。チャネルを扱うときは常に、確実に初期化する(p75)
  • 読み込み
    • nil -> ブロック
    • Openで空ではない -> 値を取得
    • Openで空 -> ブロック
    • Closed -> デフォルト値、false
    • 書き込み専用 -> コンパイルエラー
  • 書き込み
    • nil -> ブロック
    • Openで満杯 -> ブロック
    • Openで満杯ではない -> 値を書き込み
    • Closed -> panic
    • 読み込み専用 -> コンパイルエラー
  • Close
    • nil -> panic
    • Openで空ではない -> チャネルを閉じる。読み込みはチャネルの中身がなくなるまで成功する。その後読み込みはデフォルト値を読み込む
    • Openで空 -> チャネルを閉じる。デフォルト値を読み込む
    • Closed -> panic
    • 読み込み専用 -> コンパイルエラー
  • どのゴルーチンがチャネルを所有しているかはっきりさせることが重要である。所有しているゴルーチンにはチャネルに対する書き込み権限がある。利用しているだけのゴルーチンには読み込み専用権限しかない(p77)

selectでタイムアウトを実現する簡潔な方法。

package main

import (
 	"time"
 	"fmt"
)

func main() {
	var c <- chan int
	select {
	case <-c: // 決してブロックは解放されない
	case <-time.After(1 * time.Second):
		fmt.Println("Timed out.")
	}
}
Timed out.
  • もしあるゴルーチンがゴルーチンの生成の責任を負っているのであれば、そのゴルーチンを停止できるようにする責任もある(p96)
  • 外部から終了させるためのチャネルを返させるパターン
  • チャネルの合成パターン(p98)
  • パイプライン構築のベストプラクティス(p106)
  • パイプライン全体を割り込み可能にする(p111)
  • キューの真の実用性は、あるステージの実行時間が他のステージの実行時間に影響を与えないようにステージを分離することにある(p129)
  • Contextパッケージの2つの目的(p135)
    • コールグラフの各枝をキャンセルするAPIを提供する
    • コールグラフを通じてリクエストに関するデータを渡すデータの置き場所を提供する
  • Contextに保存するガイドライン(p147)
  • エラーで表示する必要のあること(p150)
    • 何が起きたか
    • いつどこでエラーが発生したか
    • 1行程度のわかりやすいメッセージ
    • さらに情報を得るにはどうすればよいか(スタックトレースなど)
  • エラーを包む必要があるのは自分のコードが有益なエラーコンテキストを追加しうるときだけである。たいていのコードでエラーを包む必要はない(p152)
  • きちんとした形式のエラーが持つべき情報をすべて含む独自のエラー型を作る(p153)
  • 並行処理のプロセスを設計するときには、タイムアウトとキャンセル処理を考慮する(p164)
  • トークンバケットアルゴリズムによる流量制限(p180)
  • 複数の流量制限ルール(時間、日、…)を適用する実装例(p187)
  • ゴルーチンの健全性を監視する実装例(p192)

感想。

  • デススパイラルの話がよくわからない(p132)
  • ハートビートの話がよくわからない(p171)

関連

なし。