Programmingバックエンド開発者

Goにおけるクロージャの実装方法、ループ内でゴルーチンを実行する際の注意点、および典型的なエラーを回避する方法は?

Hintsage AIアシスタントで面接を突破

回答

Goにおいてクロージャとは、周囲のスコープの変数を「閉じ込める」(キャプチャする)関数のことです。特に、クロージャは他の関数内で作成される無名関数でよく使用されます。

クロージャを使用する際の最も一般的な問題は、ループ内の変数をゴルーチンで使用する際の予期しない動作です:

for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }

各ゴルーチンは同じ値のiを印刷する可能性があります。これは、変数iがループの中で変更され、クロージャがその変数をキャプチャするためです。

正しい方法:

for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }

このような振る舞いは、クロージャが変数への参照(アドレス)を保持し、その値(バイ・バリュー)ではないために発生します。

引っ掛け問題

ループ内でいくつかのゴルーチンが実行され、ループ変数をキャプチャした場合、どの値が印刷されるでしょうか?

回答: すべてのゴルーチンは同じ値(多くの場合は最後の値)を印刷します。これは、ゴルーチンが変数の現在の値を参照しており、ゴルーチンが実行される時点ではループが終了しているためです。これを回避するには、変数をクロージャへのパラメータとして渡す必要があります。

例:

for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // おそらく出力されるのは: 5 5 5 5 5

知識不足による実際のエラーの例


物語

製品分析システムでは、並行ゴルーチンでデータがアノニム化され、ループ変数をキャプチャするクロージャが使用されました。その結果、すべての並行タスクが同じデータセットを処理し、統計が歪められ、不正確なレポートが生成されました。

物語

クラウドサービスのバックエンドGo統合チームは、無名関数を使用してメトリクス収集を最適化し、マップのインデックスをキャプチャしました。その結果、処理しているインデックスに対してではなく、最後に処理したインデックスのデータを収集する一部のハンドラが発生しました。

物語

配送スタートアップでは、注文の座標を更新する際にクロージャが不適切に使用され、無名関数内のスライスへのアクセス競合により、最後の注文の座標が大量に更新されてしまいました。