$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

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

最近Goの並行処理を理解する必要が出てきたので、「Go言語による並行処理」を読んだ。

並行処理とはそもそも何か、goroutineやsyncなどの並行処理のためのパーツ、さらにchannelの基本的な使い方などを学べる内容だった。いろんなパターン例も入っていて、頭の整理に活用できた。

特にchannelの所有権をどう考えると扱いやすいかという点が参考になった。channelを使うときはまず所有権 = 初期化、書き込み、閉じる責任を持つgoroutineを決定し、以下を心がけるということだった。

  • 所有するゴルーチンは次の手順を踏む
    • チャネルを初期化
    • 書き込みを行うか、他のゴルーチンに所有権を渡す
    • チャネルを閉じる
    • 上の3手順をカプセル化して、読み込みチャネルを経由して公開
  • 所有権のスコープはできる限り小さく。構造体のメンバー変数にチャネルがあると不明瞭になる
  • 読み込み側は二つだけ注意する
    • チャネルがいつ閉じられたかを把握する
    • いかなる理由でもブロックする操作は責任を持って扱う

このような感じでgoでの並行プログラミングの基本を学べるのでおすすめ。

読書ノート

- デッドロック発生の条件 500
    - 相互排他(Mutual Exclusion)
        - ある並行プロセスがリソースに対して排他的な権利をどの時点においても保持している
        - 排他制御がなければそもそもデッドロックは発生しない
    - 条件待ち(Mutual Exclusion)
        - ある並行プロセスはリソースの保持と追加のリソース待ちを同時に行なっている
    -  横取り不可(No preemption)
        - ある並行プロセスによって保持されているリソースがそのプロセスによってのみ解放される
        - 横から解放できればデッドロックは発生しない
    - 循環待ち(Circular wait)
        - ある並行プロセス(P1)は他の連なっている並行プロセス(P2)を待たなければならない。またP2はP1を待っている
- ライブロックとは並行操作を行っているけど、プログラムの状態を全く進めていないプログラム。廊下で誰かとすれ違おうとして避けたら相手も避けるをずっと繰り返している状態。プログラムとしては動いているように見えてしまう 514
- 並行処理を行う関数では、誰が並行処理を担っているか、どのように並行処理のプリミティブに対応するか、誰が同期処理を担っているかを明記する 704
- 並行性はコードの性質、並列性は動作しているプログラムの性質 784
- Goの並行処理モデルはCSPをベースにしている 909
- メモリを共有することで通信してはいけない。代わりに通信することでメモリを共有する 1039
- プリミティブ(sync)を使うか、チャンネルを使うか 1057 ⭐️
- ゴルーチンの利点は軽い。数KBのメモリ 1286
- チャネルを使う時は、所有権をどこに持たせるか決定する。所有権 = 初期化、書き込み、閉じるゴルーチン 2318 ⭐️
    - 所有するゴルーチンは次の手順を踏む
        - チャネルを初期化
        - 書き込みを行うか、他のゴルーチンに所有権を渡す
        - チャネルを閉じる
        - 上の3手順をカプセル化して、読み込みチャネルを経由して公開
    - 所有権のスコープはできる限り小さく。構造体のメンバー変数にチャネルがあると不明瞭になる
    - 読み込み側は二つだけ注意する
        - チャネルがいつ閉じられたかを把握する
        - いかなる理由でもブロックする操作は責任を持って扱う
- 並行プロセスでエラーが起きた時は、エラーを処理できる別の箇所へ送る 2984
    - Result(Error, Response)といった形でチャネル経由で送るなど
- selectであるチャネルへの書き込みが終わったらnilを代入して、もう片方の書き込みをできるようにという手段が使える 3769
- チャネルのバッファーは最終手段。初めから使うとデッドロックやライブロックなどといった同期に関する問題を隠すことがある 3875