Go言語による並行処理を読んだ

結構前に下記の本を読んだ。メモをしていたのでここにも残しておこうと思う。

Go言語による並行処理

Go言語による並行処理

前提知識

今までgoroutineを使うことはあってもいくつか作ってsync.WaitGroupで待つ、くらいのことしかしていなかった。
なぜなら、どう動くのか/いかに動かすのかの理解がなく単純なことしかやらせていなかったからだ。 本書を読んで、こ慣れた扱いも少しはできるようになったかなと思う。よかったよかった。

へーってなったところ

syncを使う?channelを使う?

goroutineを使う時にsyncパッケージを使うのか、channelを使うのか、というのは悩みどころだと思う。事実、私はよく悩んでいた。どちらでもやりたいことはできるけど使い分ける基準があるのだろうか、とgoroutineを書くときはいつもモヤモヤしていた。

本書以前にsyncパッケージのドキュメントには下記のようにある。

syncパッケージでは排他制御といった基本的な同期のためのプリミティブを提供します。Once型とWaitGroup型以外は、低水準ライブラリ内で利用されることを想定しています。高水準の同期はチャネルや通信によって行われたほうが良いでしょう。

また、公式FAQには下記のようにある。

ミューテックスに関して言えば、syncパッケージがそれを実装していますが、Goのプログラミングスタイルでは、高水準の技術を使うことを推奨しています。特に、プログラムを書く際にはある瞬間にただ1つのゴルーチンがある特定のデータの責任を持つように心がけてください。メモリを共有することで通信してはいけません。かわりに、通信することでメモリを共有しましょう。

高水準の同期とは何だろうか。
本書ではこれらsyncパッケージとchannelのどちらを使うのか、という決定木が載っている。これが非常にわかりやすい。内容は並行処理のスコープが特に意識されていると感じた。

ちなみに公式wikiに使い分けについて言及がある。

MutexOrChannel · golang/go Wiki · GitHub

他の並行処理に関する公式資料は過去記事でまとめた。

prelude.hatenablog.jp

実装パターン

goroutine雰囲気erなので、並行処理でどう実装するのか手札をあまり持っていなかった。本書は並行処理パターンという章で色んな実装パターンの実例を読むことができる。selectの使い方やchannelを上手く使うことで孫goroutineを扱ったり。さらにpipelinefan-in/fan-outなどのロジックは単純な機能を組み立てて複雑なことまで表現することのできるGoらしさを強く感じた。

chanの仕様についても理解することができる。例えばchanがnilや空の時の挙動であったり、キューとしてどう振る舞っているのかなど。chanを深く理解していなかったが、本書を読んでからはchanを使った他人の実装も読めるようになった気がする。

「並行処理をバリバリ書かないから実装パターンを手札として増やしても仕方がない」と思うこともあったが、実装パターンを多く読むことで並行処理をGoでどう使い使われるのかの想像力がかなり豊かになる点はすごく良かったと思う。さっきも書いたけど他人の書いた並行処理が読めるとレビューもしやすくなるし、ライブラリもどんどん読める。

応用

エラー伝播やハートビート、流量制限は、若干難しかった。
しかし、実際の開発を前提に置いて話が展開されるため、イメージはしやすかった。

まだまだ読み込みの浅さを感じているので、このあたりはまたいつか読み直したい。

わからなくて調べたこと

CSP

並行処理一般の理論で追いつけないところがあった。なんとなく言いたいことはわかるけれど雲を掴むような気持ちで読んでいた。

GoはCSP(Communicating Sequential Process)という理論を土台?としている。CSPはプロセスが通信によってコミュニケーションをする、というものでGoの「共有ではなく通信によりメモリを共有する」という思想の基になっている話だ(たぶん)。公式ドキュメントにCSPを土台にした背景が少し書いてある。

golang.org

わからなかった理由として、アクターモデルとの分別がついていないことがある。アクターモデルもアクター同士がメッセージを相互に送ってコミュニケーションするのでは?といまだに腑に落ちていない。
アクターモデルの理解がそもそも浅いため、いまの自分には手に負えないものだった。
アクターモデルwikiにCSPに言及している部分もあったけど、いまいちわからなかった。

ja.wikipedia.org

ただ、本書にも記載があるがGoは並行処理の複雑さに言語仕様から立ち向かってくれたおかげで、我々は簡単にコードを書くことができている。CSPの理解を深めることでGoの並行処理がより上手に書くことができることは本書が示していると思う。

今後何か意識したいところ

  • syncchannelの使い分け。
  • パターンに依拠した実装
  • goroutineを使わない選択肢を思考すること

まずは並行処理のスコープを狭くしつつ、基本に忠実に書くこと。そしてその上でパターンを使ってgoroutineの力を引き出すように書くことが求められると思う。これが逆転するとせっかく複雑さを回避しようとしているのに、スコープの広いchanに悩まされることだろう。

また、goroutineに固執せず、使わなくて良いなら使わない。goroutineは非常に大きな力だけれども、並行処理は使わない方が単純明快だ。しっかりと検証した上で必要になったら使いたい。本書で知ったことを使いたくなってもグッと堪えるのは大事だろう。