[Goのはじめ方] Part20: Goroutineの使い方

Go

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の完了を待つためにChannelssync.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間で安全にデータをやり取りしたり、処理の完了を確実に待機したりするための重要な仕組みであるChannelsselect文について学びます。

コメント

タイトルとURLをコピーしました