本文へ
ハクソク

世界を動かす技術を、日本語で。

Goの並行処理を凝縮する

元記事: 原文(antonz.org)

概要

このミニブックは Goの並行処理 に関する主要トピックの概要を解説 各トピックごとに インタラクティブな例 を用意 初心者向けではなく、 復習用・リファレンス として設計 Goroutines、Channels、Select、Pipelines など幅広く網羅 AI未使用の 純粋な技術解説 を特徴とする

Go並行処理ミニブック 概要

  • Go言語 における並行処理の重要トピックを簡潔に整理

    • Goroutines
    • Channels
    • Select
    • Pipelines
    • 時間管理(Time)
    • Context
    • Wait groups
    • データ競合・Race conditions
    • Mutexes
    • Semaphores
    • シグナリング
    • Run once
    • オブジェクトプール
    • Atomics
    • Testing
    • Scheduling
    • Diagnostics
    • 総括(Final thoughts)
  • 各トピックは 実用的なサンプルコード 付き、自由に編集・実行可能

  • Goの並行処理復習 用、初学者向けの詳細な入門書ではない

  • より基礎から学びたい場合は「Gist of Go: Concurrency」参照推奨

  • PDF版 も用意、静的なサンプル例を収録


Goroutines

  • Goの並行処理の基盤 はgoroutine(goキーワードで開始する関数)
  • goroutineは 軽量スレッド、OSスレッドより遥かに多く作成可能
  • 各goroutineは 独立して動作、main関数もgoroutineとして暗黙的に開始
  • mainが終了すると、他のgoroutineも 強制終了
  • sync.WaitGroup で複数goroutineの終了待ち
    • Add(n)でカウンタ加算
    • Done()でカウンタ減算
    • Wait()でカウンタ0までブロック
  • WaitGroup.Go で自動的にカウンタ管理・goroutine実行

Channels

  • goroutine間で値をやり取りする チャネル
    • 送り側(ch <- val)と受け側(<-ch)で 同期的通信
    • 送信側は受信側が値を受け取るまでブロック
  • 出力チャネル のパターン
    • 関数内部でgoroutineを立ち上げ、チャネルを返すことで値を逐次送信
  • チャネルのクローズ
    • close()で全データ送信完了を通知
    • 受信側は「値, ok」形式で状態判定
    • クローズ済みチャネルへの再クローズ・書き込みはpanic
    • 必要な場合以外はclose不要、未使用ならGCで自動解放
  • チャネルのrange
    • rangeで自動的に値を取得し、クローズでループ終了
  • 方向指定チャネル
    • chan(双方向)、chan<-(送信専用)、<-chan(受信専用)
    • 関数引数で方向を明示し事故防止
  • バッファ付きチャネル
    • FIFOキューとして利用、バッファが空なら送信ブロック・値があれば受信即時
    • len()/cap()でサイズ・格納数確認可能
    • クローズ後はバッファ消費後にゼロ値・false返却
  • nilチャネル
    • 初期値はnil、読み書きは永久ブロック
    • クローズはpanic

Select

  • select文 はチャネル専用のswitch
    • 複数caseの中でブロックしないものを実行
    • 複数準備完了ならランダム選択
    • 全ブロック時にdefaultあれば実行、なければ待機
  • パイプライン合流(merge) や キャンセル、 ノンブロッキング送信 で活用
    • 例:merge関数で複数チャネル統合
    • 例:cancelチャネルでgoroutineの早期終了
    • 例:default使用でチャネルビジー時のエラー返却

Pipelines

  • パイプライン は各処理段階がチャネルで接続される一連の処理系
    • Reader:データ入力
    • Processor:加工・変換・集計
    • Writer:出力
  • 各段階は チャネルでデータ受け渡し
  • 完了通知チャネル(done channel) で処理終了を他goroutineへ通知
  • キャンセルチャネル(cancel channel) で早期停止
  • エラー処理 は3パターン
    • エラー発生時に即return
    • 結果型Resultで値とエラーをまとめて返す
    • エラーチャネルで分離して収集

時間管理(Time)

  • timeパッケージ は並行処理でのタイムアウト・遅延制御に便利
  • time.After() で指定時間後に値を受け取れるチャネルを取得
  • withTimeout関数 で処理のタイムアウト実装が容易

この他にも Context、Wait groups、Mutexes、Semaphores、Signaling など、Goの並行処理に不可欠な要素を多数網羅。 各トピックの詳細やサンプルコードは、実際のGoプログラムでそのまま応用可能。

提供: ハクソク · 要約更新:

AIによる日本語要約です。内容は原文とあわせてご確認ください。

Hacker Newsの議論を読む

Hackerたちの意見

Goの同時実行性とスレッド処理は、他の言語と比べるとまるで魔法みたいだね。俺はgoroutine中毒で、リハビリなんて受ける気はないよ。何年も観察してきたけど、こんな感じでスレッドで何かが起こる言語は他にないと思う。

└

あなたの経験では、何かデメリットはありますか?

└

その前にHaskellを学んだけど、正直Goの同時実行性は似てるけど、STMがない分、明らかに劣化してるね。チャンネルやselectをSTMで実装できるから、これらは標準ライブラリに含まれなくてもいいし。チャンネルを二回閉じた時の挙動みたいな議論の余地があるデザイン選択も、自分で決められるんだよね!STMからミューテックスの管理に移るのは、抽象化の力が明らかに落ちるし。Haskellの同時実行性デザインは、本当に魔法みたいだ。

└

スレッドで何かが起こるという点では、ほとんどのグリーンスレッドスタイルの同時実行実装を説明してるんじゃない?

└

Erlangが一言言いたがってるよ。

└

ErlangやElixirをBEAMで使うのはどう?WhatsAppはErlangとBEAMで10億人以上のユーザーにスケールしたらしいよ。Redditが使ってるRabbitMQもErlangとBEAMだし、DiscordはElixirとBEAMを使ってる。最近BEAMのことを調べてみたけど、面白い話だね。エリクソンのコンピュータサイエンス研究所は1990年代初頭にすごい製品を生み出してた。彼らの目標はエリクソンの電話交換機での99.999%の信頼性だったんだって。エリクソンのジョー・アームストロング(面白い人だよ)によると、AXD301 ATMスイッチは2002年にErlangとBEAMを使って9ヶ月間で99.9999999%を達成したらしい。それって24ミリ秒のダウンタイムに相当するよ。

└

Javaは、バーチャルスレッドや構造化並行性、不変レコードと不変値型を使って、もっと良くやってると思う。ゴーランはメモリ安全じゃないしね。

└

Kotlinのコルーチンは基本的にGoのと同じAPIを持ってる。でも、特定のスレッド(例えばUIのメインスレッド)にコルーチンの実行を制限する機能もあるんだ。それから構造化並行性を追加して、Goのコンテキストと同じ問題を大体解決してるけど、もっとエレガントに見えるかも。

多くの人にとって、何をすべきかを学ぶのも大事だけど、Goでやってはいけないアンチパターンを読む方が役立つことが多いよ。このデータレースパターンに関する記事は特におすすめだね。: https://www.uber.com/us/en/blog/data-race-patterns-in-go/

Goの同時実行性は見た目はシンプルだけど、selectや適切なエラーハンドリングをマスターするには練習が必要だね。このテーマがうまくまとめられてるのを見るのはいいね。

もう10年以上Goを書いてるけど、チャンネルがいまだに「わかった」って感じにならないんだよね。使うたびにマニュアルを見に行かなきゃいけないし、パターンも全然明白じゃない。言語の他の部分はすごくわかりやすいのに、なんでだろう。JavaでスレッドやRunnableを管理しすぎたせいで脳が腐っちゃったのかも。

└

言ってしまえば、Javaのバーチャル(グリーン)スレッドを構造化並行性やフューチャーで管理するのは、優れたアプローチだと思う。

Hacker Newsで議論の続きを見る

同じ出典の記事