Golang Cafe #21.1 まとめ 続「Twelve Go Best Practices」を読む(要約編)

2014/03/15に開催された「Golang Cafe #21」についてのまとめです。
前回に引き続きTwelve Go Best Practicesを読んでいきました。一応今回でTwelve Go Best Practicesを読み終えました。

APIの中では同時(並行)実行を避ける

goroutineを用いた処理を行う場合、API内でgoroutineを用いて並列処理を行うのではなく、APIは同期メソッドとして公開して、呼び出す側でgoroutineを用いてAPIを呼び出すようにします。
こうすることでAPIを呼び出す側で呼び出しと結果の受取方法を決める(そのまま呼び出す、goroutineで呼び出すなど)ことができます。(APIの中がわかりにくい処理はしない)

状態を管理するためにgoroutineを使用する

goroutineで実行されている処理と通信するためにはchan(チャンネル)を使用します。バッファを使用しないチャンネルの場合、チャンネルへのアクセスがブロックされます(書き込み時にすでに値が格納されている場合、あるいは読み込み時にまだ値が格納されていない場合)。これを利用してgorutineでの処理をブロックしたり、メイン側での処理をブロックしたりすることができます。

バッファリングされたチャンネルを使用してgorutineのリークを避ける

バッファリングされていないチャンネルを複数のgoroutineで使用する場合チャンネルがGCで回収されないパターンがあります。
goroutineでの処理AとBがあったとして、両方の処理でバッファリングされていないチャンネルを共用する場合です。処理Aがチャンネルに書き込み、メイン側でチャンネルから読み込みます。その際にエラー等で関数をそのまま抜けてしまった場合に処理Bで参照しているチャンネルの参照が回収不能になってしまいます。
これを防ぐためにチャンネルをバッファリングします。処理AとBがある場合はバッファ2とします。こうすることで処理AおよびBのチャンネルへの書き込みが終わってからメイン側に処理が移るので、関す終了時に不要なチャンネルへの参照がなくなります。
この内容は難しいので資料のソースコードとともに別エントリでソースコード説明編を書きたい(恐らく書ける)と思います。

終了用のチャンネルを使用してgoroutineのリークを避ける

「バッファリングされたチャンネルを使用」例では必要なバッファ数が事前に分かっていたのでチャンネル作成時にバッファ数を指定することができましたが、バッファ数がわからない場合はどうしたら良いのか、というのが今回の内容です。
goroutine終了用にチャンネルを1つ用意しておきます。各goroutineで終了用チャンネルから受信できるようにしておき、メイン側の処理で

defer close(終了用のチャンネル)

としておきます。
こうしておくと先のバッファリングされていないチャンネル例の用にエラー等で関数を抜ける際に、終了用のチャンネルがクローズされ、そのチャンネルがクローズされると、各goroutineで終了用チャンネルからの受信が行われ、各goroutineが正常に終了しチャンネルへの参照も開放されます。goroutineで処理を起動したら終了までしっかり確認しろというような意味合いでしょうか。

まとめ

今回でTwelve Go Best Practicesをひと通り読み終えました。
Go言語を始めたことに+TakashiYokoyama氏に何度も質問していたことが解決したり大変勉強になりました。
何よりgoroutineへの知識の無さ、勉強不足に気づくことができました。
Go言語はまだまだ奥が深いです。
次回は通常通りで3月23日(日)の開催のようです。

読んでおいたほうがよい書籍

Twelve Go Best Practices」を読み進める上でGo言語の知識はもちろん必要ですが、以下の書籍にも目を通しておくほうが良いかもしれません。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)

いずれもJavaを対象に書かれていますが、考え方は適用できます。