goroutine
Go の軽量スレッドで、数十万規模の並行処理を低コストで実現する
goroutine とは
goroutine は Go の軽量スレッドで、go キーワードで関数を並行実行する。OS スレッド (数 MB のスタック) と異なり、goroutine は数 KB のスタックで起動し、必要になった分だけランタイムがスタックを伸縮させる。公式 FAQ が「同一アドレス空間に数十万の goroutine を作るのは実用的」と述べる桁で、同じ数を OS スレッドで作れば先に資源が尽きる。Go ランタイムが goroutine を OS スレッドにマッピングする (M:N スケジューリング)。
OS スレッドとの比較
OS スレッドとの主な違いを以下に比較する。
| 観点 | OS スレッド | goroutine |
|---|---|---|
| スタックサイズ | 1〜8 MB (固定) | 数 KB から動的に伸縮 |
| 生成コスト | 高い | 低い |
| 同時実行数 | 数千 | 数十万 |
| スケジューリング | OS カーネル | Go ランタイム |
| コンテキストスイッチ | 遅い (カーネル空間) | 速い (ユーザー空間) |
基本的な使い方
基本的な使い方のコード例を示す。
func main() {
go processOrder("order-1") // goroutine で並行実行
go processOrder("order-2")
go processOrder("order-3")
time.Sleep(time.Second) // goroutine の完了を待つ (簡易版)
}
WaitGroup で完了を待つ
WaitGroup で完了を待つのコード例を示す。
func main() {
var wg sync.WaitGroup
orders := []string{"order-1", "order-2", "order-3"}
for _, id := range orders {
wg.Add(1)
go func(id string) {
defer wg.Done()
processOrder(id)
}(id)
}
wg.Wait() // 全 goroutine の完了を待つ
}
channel との組み合わせ
channel との 組み合わせのコード例を示す。
func fetchUser(id string, ch chan<- User) {
user := db.GetUser(id) // DB から取得
ch <- user // channel に送信
}
func main() {
ch := make(chan User, 3)
go fetchUser("1", ch)
go fetchUser("2", ch)
go fetchUser("3", ch)
for i := 0; i < 3; i++ {
user := <-ch // channel から受信
fmt.Println(user.Name)
}
}
Node.js の async/await との比較
Node.js の async/await との主な違いを以下に比較する。
| 観点 | goroutine | async/await (Node.js) |
|---|---|---|
| モデル | 軽量スレッド (プリエンプティブ) | イベントループ (協調的) |
| CPU バウンド | 複数コアへ分散して同時に計算できる | 1 スレッドで動き、重い計算の間は他が止まる |
| I/O バウンド | 待っている間に他の goroutine が進む | 待っている間に他のタスクが進む |
| メモリ共有 | あり (Mutex で保護) | なし (シングルスレッド) |
Lambda (Go ランタイム) での goroutine
Lambda (Go ランタイム) での goroutine のコード例を示す。
func handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
ch := make(chan string, 2)
go func() { ch <- fetchFromDB(event.PathParameters["id"]) }()
go func() { ch <- fetchFromCache(event.PathParameters["id"]) }()
result := <-ch // 先に返った方を使う
return events.APIGatewayProxyResponse{StatusCode: 200, Body: result}, nil
}
よくある間違い
- goroutine のリーク: channel の受信側がいないと goroutine が永遠にブロック
- データ競合: 共有変数への同時アクセス →
sync.Mutexや channel で保護 - 安いのは生成と切り替えだけ: 関数呼び出しあたりの追加コストは数命令程度だが、goroutine が抱えるスタックとヒープ上のデータは残る。数十万を並べる設計では 1 本あたりの保持データ量が支配的になる
goroutine の背景や設計思想は関連書籍に詳しい。
この記事は役に立ちましたか?
関連用語
Go
Google が開発したシンプルで高速なコンパイル言語で、goroutine による軽量な並行処理が特徴
channel
Go 言語で goroutine 間のデータ通信と同期を行う型付きパイプ
並行処理 (Concurrency) とは - 並列処理 (Parallelism) との違い
Concurrency (並行処理) の意味を解説。複数のタスクを論理的に同時進行させる手法で、物理的に同時実行する並列処理 (Parallelism) とは区別される。Node.js / Go / Rust の並行処理モデルの違いも紹介。
スレッドプール
事前に生成したスレッドを再利用し、スレッド生成のオーバーヘッドを削減する並行処理パターン
プロセスとスレッドの違いとは - メモリ空間 / 生成コスト / 通信方法を比較
プロセスとスレッドの違いをメモリ空間 / 生成コスト / 通信方法の 3 軸で比較。Node.js での使い分け、Lambda の実行環境、データ競合とコンテナの PID 1 の落とし穴まで解説
Worker Threads
Node.js で CPU 集約的な処理をメインスレッドをブロックせずに別スレッドで実行する仕組み
関連する記事
技術書ランキングの決定版はどれか - IT エンジニア本大賞の歴代大賞と言及数データで読む
技術書ランキングを探している人向けに、検証可能な 2 つのデータ (IT エンジニア本大賞の歴代大賞 2014-2026 年と、当サイトの Qiita / Zenn 言及数ランキング) を整理。歴代受賞作の一覧表と、ランキングを選書に活かす方法を解説します。
技術書の比較レビューの書き方 - 同じテーマの本を読み比べる
同じテーマの技術書を 2〜3 冊読み比べて比較レビューを書く方法と、比較軸の設定、読者に価値を届ける構成を紹介します。
技術書の読む順番戦略 - 複数冊を組み合わせて理解を加速させる
技術書を 1 冊ずつ読むのではなく、複数冊を戦略的に組み合わせることで、1 冊では届かない理解の深さと広さに達する方法を解説します。