ゴルーチン は、Go ランタイムによって管理される軽量で同時実行可能な関数です。起動するには、関数呼び出しの前に go を付けるだけです。ゴルーチンは Go の特徴的な並行性機能です — OS スレッドよりはるかに軽量なため、数千から数百万のゴルーチンを実行できます。
ゴルーチンの起動
{
fmt.Println()
}
sayHello()
{
fmt.Println()
}()
go キーワードは新しいゴルーチン上で関数を開始し、すぐに戻ります — 呼び出し元のコードは待機せずに続行します。
OS thread: ~1–2 MB of memory each → thousands max
Goroutine: ~2 KB initial stack (grows as needed) → MILLIONS feasible
The Go runtime SCHEDULES many goroutines onto a small pool of OS threads (M:N).
ゴルーチンは OS スレッドではありません — Go スケジューラは多くのゴルーチンを少数の OS スレッド上で多重化します。これにより、それらは驚くほど軽量になり、大規模な並行処理が可能になります(例:数百万の接続を処理するサーバーでは、接続ごとに 1 つのゴルーチン)。
func main() {
go sayHello() // ❌ main might EXIT before this goroutine runs → nothing printed
// program ends immediately
}
main が戻ると、プログラムが終了し、実行中のゴルーチンが全て強制終了されます。調整が必要です。
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1) // increment the counter
go func(n int) {
defer wg.Done() // decrement when this goroutine finishes
fmt.Println("worker", n)
}(i) // pass i as an argument (avoid the loop-var capture bug)
}
wg.Wait() // BLOCK until all goroutines call Done()
sync.WaitGroup はゴルーチンのセットの完了を待つ標準的な方法です。
ch := make(chan string)
go func() { ch <- "result" }() // send a result on the channel
msg := <-ch // receive (blocks until a value arrives)
Go の哲学: "メモリを共有することで通信するのではなく、通信することでメモリを共有する" — 共有変数とロックの代わりに、チャネルを使用してゴルーチン間でデータを渡します。
ゴルーチンは Go の定義的な機能であり、Go が並行性に富んだネットワークシステムで優れている主な理由です。
それらの極端な軽量性(スレッドのメガバイトに対するキロバイト、OS スレッド上で M:N にスケジュールされる)により、大規模な並行処理が実用的かつ容易になります — サーバーは、リソースの懸念なく、リクエストまたは接続ごとにゴルーチンを生成できます。これは OS スレッドでは困難またはほぼ不可能です。
それらを起動する方法(go)、同期化の重大な必要性(main の終了はゴルーチンを強制終了します。待機するには WaitGroup を使用)、そして共有メモリの代わりにチャネルによる通信を優先する Go の設計哲学を理解することは、正しい並行 Go コードを記述するために不可欠です。
ゴルーチン(およびそれらの周辺パターン)は Go の価値提案の中心であり、言語の中で最も重要で頻繁にテストされるトピックの一つです。