Goroutine の基本
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 59/107。
goroutineは、Goランタイムによって管理される軽量な実行スレッドです。大量のメモリを消費する従来のスレッドとは異なり、goroutineはわずか数キロバイトのスタック領域で開始されるため、数千ものgoroutineを並行して実行できます。
goroutineを開始するには、単に関数呼び出しの前に go キーワードを置きます:
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // 並行して実行されます
go sayHello("Bob") // 並行して実行されます
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}go sayHello("Alice") が実行されるとき、sayHello の終了を待ちません。その代わりに、sayHello がバックグラウンドで実行されている間、すぐに次の行へと進みます。これが time.Sleep が必要な理由です。これがないと、ゴルーチンが完了する前に main が終了してしまいます。
無名関数を使用してゴルーチンを起動することもできます:
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")main関数自体がゴルーチン内で実行されます。main関数が終了すると、他のすべてのゴルーチンは即座に終了させられ、それらが完了する機会は与えられません。time.Sleep を使用するのは一時的な解決策です。今後のレッスンでは、チャネルやウェイトグループを使用した適切な同期手法について学びます。
チャレンジ
簡単goroutineを使用して複数のタスクを並行して実行するタスクランナーを構築しましょう。メインプログラムが続行している間にタスクがバックグラウンドで実行されるシンプルなシステムを作成し、goroutineがどのように並行実行を可能にするかを実証します。
コードは2つのファイルに分けて構成します:
tasks.go: タスクの実行ロジックを定義します。Name(string) とDuration(int、ミリ秒単位) フィールドを持つTask構造体を作成します。TaskにRun()メソッドを実装します。このメソッドは、タスクの期間(duration)だけスリープすることで作業をシミュレートし、その後Task [Name] completedと出力します。各タスクの
Run()メソッドを goroutine として起動するRunAllTasks(tasks []Task)関数を作成します。すべての goroutine を起動した後、それらが完了するのを待つために500ミリ秒間スリープし、その後All tasks launchedと出力します。main.go: タスク情報を読み取り、並行実行をオーケストレートします。タスクの数を読み取り、次に各タスクの名前と期間を読み取ります。タスクを作成して
RunAllTasksに渡します。関数が戻った後、Main function doneと出力します。
以下の入力が提供されます:
- 1行目:タスクの数(整数)
- 続く行:各タスクについて、タスク名とその期間(ミリ秒)の2行
例えば、以下のように与えられた場合:
3
Download
100
Process
50
Upload
75出力には、タスクの完了(並行実行のため順序は異なる場合があります)が表示され、その後に完了メッセージが表示されるはずです:
Task Process completed
Task Upload completed
Task Download completed
All tasks launched
Main function done注意:goroutineは並行して実行されるため、通常、期間の短いタスクが先に終了します。タスクの完了順序はそれらの期間に依存し、短いタスクは長いタスクよりも先に完了します。
遅延には time.Sleep を time.Duration および time.Millisecond と共に使用してください。RunAllTasks 内のスリープがないと、goroutineが作業を完了する前に関数が戻ってしまうことに注意してください。
チートシート
ゴルーチン (goroutine) は、Goランタイムによって管理される軽量な実行スレッドです。ゴルーチンはわずか数キロバイトのスタック空間で開始されるため、数千ものゴルーチンを並行して実行することができます。
ゴルーチンを開始するには、関数呼び出しの前に go キーワードを記述します。
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // 並行して実行されます
go sayHello("Bob") // 並行して実行されます
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}go sayHello("Alice") が実行されると、sayHello の終了を待ちません。代わりに、sayHello がバックグラウンドで実行されている間に、即座に次の行へと進みます。
匿名関数を使用してゴルーチンを起動することもできます。
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")main 関数自体もゴルーチンの中で実行されます。main 関数が終了すると、他のすべてのゴルーチンは即座に終了します。main 関数が終了する前にゴルーチンが完了できるようにするには、time.Sleep を使用します。
自分で試してみよう
package main
import (
"fmt"
)
func main() {
// タスクの数を読み込む
var numTasks int
fmt.Scanln(&numTasks)
// TODO: タスクを保持するためのスライスを作成する
// TODO: 各タスクの名前と期間を読み込む
// 各タスクについて:
// - タスク名を読み込む
// - 期間(整数)を読み込む
// - Taskを作成し、スライスに追加する
// TODO: タスクのスライスを使用してRunAllTasksを呼び出す
// TODO: "Main function done" と出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー