Programmingバックエンド開発者

Goにおけるゴルーチンとは何かを説明してください。それはどのように内部で動作し、他の言語のスレッドと何が違うのですか?

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

答え

ゴルーチンは、Goランタイムによって管理される軽量な実行スレッドです。新しいゴルーチンを起動するには、関数呼び出しの前に go キーワードを使います。内部では、スタックとタスクの状態を記述する構造体が作成され、ゴルーチンプランナーのキューに追加されます。

OSのスレッドとは異なり、ゴルーチンは非常に小さい初期スタック(通常2KB)を持ち、必要に応じて自動的にスタックが拡張されます。Goのスケジューラーは、これらをOSの利用可能なスレッドに自動的に割り当てます(M:Nモデル)。

スレッドとの主な違い:

  • より迅速に作成でき、メモリも少なくて済む
  • Goランタイムによってスケジュールされ、OSではない
  • 自動的にコアにスケールされる
  • 同期はチャネルを通じて実装されており、多くの競合条件を防ぎます

ゴルーチンとチャネルの使用例:

package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d started job %d ", id, j) time.Sleep(time.Second) fmt.Printf("worker %d finished job %d ", id, j) results <- j * 2 } } func main() { jobs := make(chan int, 5) results := make(chan int, 5) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= 5; j++ { jobs <- j } close(jobs) for a := 1; a <= 5; a++ { <-results } }

騙しの質問

Goのゴルーチンは、複数のコアで並行して実行できますか?

「いいえ、Goはグリーンスレッドを使用しているのでできません」といった誤った答えがよく見られます。実際には、環境変数を使用するか、runtime.GOMAXPROCS(n)を呼び出すことで、Goはゴルーチンの実行を利用可能なすべてのプロセッサコアに並行処理できます。

例:

import "runtime" func main() { runtime.GOMAXPROCS(4) // 4つのコアを使用可能にします ... }

このテーマの細かい点を知らないための実際のエラーの例


物語

Goのバックエンドサービスプロジェクトでゴルーチンを使ったワーカープールが実装されましたが、プログラマーたちは同時に動作するゴルーチンの数を制限することを忘れました。その結果、負荷が増えるとアプリケーションは数千のゴルーチンを起動し、メモリ不足に陥りサービスがクラッシュしました。問題は、アクティブなゴルーチンの制限を設けることで解決されました(たとえば、セマフォまたはワーカープールを使って)。


物語

ある従業員がゴルーチン間でデータを誤って同期させ、ミューテックスやチャネルを使用せずに通常のグローバル変数を使いました。これによりデータ競合(レースコンディション)が発生し、支払い処理中に時折エラーが発生しました。問題は、プロダクションで起動した後にしか発見されませんでした。


物語

パーサーサービスで、nilチャネルをselectに渡すタイミングを逃しました:チャネルが閉じられた後、selectはデータを待ち続けてブロックし、一部のゴルーチンが「ハング」しました。nilを閉じたチャネルに割り当て、selectの正しい処理を行うことで修正しました。