メインコンテンツにスキップ
モジュール 7 · レッスン 1 / 5コース内の 31/50~12 min
モジュールのレッスン (1/5)

ゴルーチン: 軽量の並列処理

ゴルーチン は、Go によって管理される同時実行の単位です。 ランタイム。関数の前に go キーワードを付けて起動します。 電話:

Go
go greet("Anna")         // chiamata a funzione esistente
go func() {              // funzione anonima
    fmt.Println("hi")
}()

go ステートメントは 即時 を返します。ゴルーチンは で開始されます。 並行して、呼び出し側は続行します。

なぜ「軽い」のか

  • 初期スタック ~2KB (必要に応じて最大数百 MB まで動的に増加します)。
  • ランタイムによって少数の OS スレッドに 多重化されます (GOMAXPROCS)。
  • 数千/数百万のゴルーチンは日常的なものです。 OS スレッドではそれは不可能です。

概念的には、これは「同時ファイアアンドフォーゲット」ですが、 調整するための慣用的なツール (チャネル、sync.WaitGroup、 context)。

メインは待ちません

Go
func main() {
    go func() { fmt.Println("ciao dalla goroutine") }()
    // main esce subito: niente garanzia che la goroutine completi
}

main が返されると、プロセスは終了します。ゴルーチンは取得されません。 「仕事を終える」ために、彼らはただ一緒に殺されるだけです。 プロセス。明示的な同期が必要です。

同期: プレビュー

次のレッスンで説明する 3 つのツール:

Go
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
    work()
    done <- struct{}{}
}()
<-done   // attendi

// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()

// 3) context: cancellazione/timeout propagati

「古典的な」ループ内クロージャのバグ

Go 1.21 まで、これはよくある問題でした。

Go
for i := 0; i < 3; i++ {
    go func() { fmt.Println(i) }()  // stampa "3 3 3" su versioni vecchie
}

Go 1.22 から、for 変数がそれぞれの環境で新たに作成されます。 反復するため、バグはデフォルトで修正されます。古いコードベースでは、 まだ修正パターンが表示されます。

Go
for i := 0; i < 3; i++ {
    i := i           // shadow esplicito
    go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
    go func(n int) { fmt.Println(n) }(i)
}

Goroutine != OS スレッド

側面ゴルーチンOSスレッド
初期スタック~2KB1 ~ 8 MB (開始時に割り当てられます)
創造ナノ秒マイクロ秒
スケジューリングGo ランタイム (連携)カーネル (プリエンプティブ)
実用的なカウント10万以上数千

単一の OS スレッドで多数のゴルーチンをホストできます。ランタイムが動く 必要に応じてスレッド間でゴルーチンを実行します。

試してみてください

運動#go.m7.l1.e1
試行回数: 0読み込み中…

ゴルーチンとして挨拶関数を起動し、メインを終了する前に時間とともに 100 ミリ秒待機します。スリープします。

エディターを読み込み中…
ヒントを表示

関数呼び出しの前に「go」を付けると、ゴルーチンが起動されます。

3 回の試行後に解決策が利用可能になります

運動#go.m7.l1.e2
試行回数: 0読み込み中…

「hi」を出力するゴルーチンとして匿名関数を起動します。

エディターを読み込み中…
ヒントを表示

匿名関数は、末尾に `()` を付けて呼び出されます: `go func(){...}()`。

3 回の試行後に解決策が利用可能になります

Quiz#go.m7.l1.e3
準備完了

goroutine が完了する前に「main」が終了するとどうなりますか?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
回答の選択肢

要約

  • go f() は f() を同時に起動します。すぐに戻ってきます。
  • Goroutine = 初期サイズは約 2 KB、いくつかの OS スレッドに多重化されます。
  • main は待機しません。同期 (チャネル/待機グループ/コンテキスト) が必要です。
  • time.Sleep は単なる教訓であり、運用環境での同期には決して使用されません。
  • Go 1.22 以降、for 変数は反復ごとに「最新」になります (「3 3 3」バグはなくなりました)。