Step 6: 並行処理(GoroutinesとChannels)
Go言語の大きな特徴の一つに、並行処理を簡単に扱えることがあります。並行処理とは、複数の処理を同時に(または見かけ上同時に)進めることです。これにより、プログラムの応答性を高めたり、処理時間を短縮したりできます。
このセクションでは、Goの並行処理の主役であるGoroutine(ゴルーチン)について学びます。
Goroutineとは?🤔
Goroutineは、Goプログラム内で並行して実行される関数のことです。OSが管理する「スレッド」と似ていますが、GoroutineはGoのランタイムによって管理され、より軽量であるという特徴があります。
- 軽量: スレッドよりも少ないメモリ(初期スタックは約2KB)で動作し、起動や破棄のコストも低いです。
- 多数実行可能: 軽量なため、数千、数万、あるいはそれ以上のGoroutineを同時に実行できます。
- 簡単: 複雑なスレッド管理を意識することなく、簡単に並行処理を記述できます。
すべてのGoプログラムは、少なくとも1つのGoroutine(`main`関数を実行するGoroutine)から始まります。
Goroutineの使い方: `go` キーワード
Goroutineを起動するのは非常に簡単です。関数呼び出しの前に go
キーワードを付けるだけです。
go 関数名(引数...)
これで、指定した関数が新しいGoroutineとして、呼び出し元の関数と並行して実行されます。
基本的な例
簡単な関数をGoroutineとして実行してみましょう。
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello from Goroutine! 👋")
}
func main() {
// sayHello関数をGoroutineとして起動
go sayHello()
fmt.Println("Hello from main function! メイン関数より")
// 少し待機しないと、Goroutineが実行される前に
// main関数が終了してしまう可能性がある
time.Sleep(1 * time.Second)
fmt.Println("Main function finished.")
}
実行結果の例(順序は変わることがあります):
Hello from main function! メイン関数より
Hello from Goroutine! 👋
Main function finished.
注意点:
main
関数が終了すると、他のすべてのGoroutineも強制的に終了します。- 上の例では
time.Sleep()
を使ってmain
関数を意図的に待機させていますが、これは確実な方法ではありません。実際の開発では、Goroutineの完了を待つためにChannelsやsync.WaitGroup
といった同期機構を使います(これらは次のセクションで学びます)。
複数のGoroutineを実行する
複数のGoroutineを起動することも簡単です。
package main
import (
"fmt"
"time"
)
func printNumbers() {
for i := 1; i <= 5; i++ {
fmt.Printf("数字: %d\n", i)
time.Sleep(100 * time.Millisecond) // 少し待機
}
fmt.Println("数字 Goroutine 終了")
}
func printLetters() {
for char := 'a'; char <= 'e'; char++ {
fmt.Printf("文字: %c\n", char)
time.Sleep(150 * time.Millisecond) // 少し待機
}
fmt.Println("文字 Goroutine 終了")
}
func main() {
fmt.Println("Main 開始")
go printNumbers() // 数字を出力するGoroutine
go printLetters() // 文字を出力するGoroutine
// Goroutineが終わるのを待つ (ここでも簡易的にSleep)
time.Sleep(1 * time.Second)
fmt.Println("Main 終了")
}
実行結果の例(出力はインターリーブします):
Main 開始
数字: 1
文字: a
数字: 2
文字: b
数字: 3
数字: 4
文字: c
数字: 5
数字 Goroutine 終了
文字: d
文字: e
文字 Goroutine 終了
Main 終了
この例では、数字を出力する処理と文字を出力する処理が並行して実行され、出力が混ざり合っている(インターリーブしている)ことがわかります。これもGoroutineが並行に動作している証拠です。
無名関数とGoroutine
go
キーワードは、無名関数(名前のない関数)に対しても使えます。
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Main 開始")
// 無名関数をGoroutineとして起動
go func(message string) {
fmt.Println(message)
}("こんにちは、無名Goroutineです!🚀")
// 終了待ち
time.Sleep(50 * time.Millisecond)
fmt.Println("Main 終了")
}
実行結果:
Main 開始
こんにちは、無名Goroutineです!🚀
Main 終了
ちょっとした処理を並行実行したい場合に便利です。
まとめと次のステップ ✨
このセクションでは、Goの並行処理の基本であるGoroutineについて学びました。
- Goroutineは軽量な実行単位で、
go
キーワードで簡単に起動できます。 - 複数の処理を並行して実行することで、プログラムの効率や応答性を向上させることができます。
main
関数が終了すると他のGoroutineも終了するため、同期が必要になります。
time.Sleep
での待機は一時的な手段です。次のセクションでは、Goroutine間で安全にデータをやり取りしたり、処理の完了を確実に待機したりするための重要な仕組みであるChannelsとselect文について学びます。
コメント