goroutine で関数を並行に実行する
Go の最大の特徴は、軽量な並行処理の仕組みである goroutine です。関数の前に go キーワードをつけるだけで、その関数を別の goroutine として並行に実行できます。
goroutine の基本
通常の関数呼び出しは、その関数が終わるまで次の行に進みません。しかし go をつけると、関数の完了を待たずにすぐ次の処理へ進みます。
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello!")
}
func main() {
go sayHello()
fmt.Println("main continues...")
time.Sleep(100 * time.Millisecond)
}このコードを実行すると、main continues... と Hello! が出力されます。順番は実行タイミングによって変わることがあります。
time.Sleep を入れているのは、main 関数が終了すると全ての goroutine も強制終了してしまうからです。実際のプログラムでは Sleep ではなく、WaitGroup や channel を使って goroutine の終了を待ちます。
匿名関数を goroutine で実行する
名前のない関数(匿名関数)をその場で goroutine として実行することもできます。
package main
import (
"fmt"
"time"
)
func main() {
go func() {
fmt.Println("匿名関数が実行されました")
}()
go func(msg string) {
fmt.Println(msg)
}("引数も渡せます")
time.Sleep(100 * time.Millisecond)
}匿名関数に引数を渡す場合は、関数定義の直後に () で値を渡します。
goroutine はとても軽い
OS のスレッドは 1 つあたり数 MB のメモリを消費しますが、goroutine は数 KB 程度です。そのため、数千〜数万の goroutine を同時に動かすことも珍しくありません。
メモリ消費が大きい(数 MB)。生成コストも高く、大量に作るのは難しい。
メモリ消費が小さい(数 KB)。Go のランタイムがスケジューリングするため、大量に作っても効率的に動作する。
この軽量さが、Go で並行処理を気軽に書ける理由になっています。
注意点
goroutine を使うときに気をつけることがあります。
まず、main 関数が終わると全ての goroutine も終了します。goroutine の完了を待つ仕組みが必要です(WaitGroup や channel を使います)。
また、複数の goroutine から同じ変数を読み書きすると、データ競合が起きる可能性があります。これを防ぐには Mutex や channel を使います。
これらの対処法は別の記事で詳しく説明します。