ゴルーチンは Go 設計の基礎であり、同時プログラミングのための強力なメカニズムを提供します。 軽量のコルーチンとして、タスクの並列実行が簡素化されます。 goroutine の起動は簡単です。関数呼び出しの前に go
キーワードを付けて、非同期実行を開始するだけです。メインプログラムはゴルーチンの完了を待たずに続行されます。
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
同時実行性: 単一の CPU 上で複数のタスクを一見同時に管理する機能。 CPU はタスク間を急速に切り替え、並列実行のような錯覚を生み出します。 微視的には連続的ですが、巨視的には同時的に見えます。
並列処理: 複数の CPU にわたる複数のタスクの真の同時実行により、CPU リソースの競合が排除されます。
プロセス: 独自のリソース (メモリ、ファイルなど) を備えた自己完結型の実行環境。 プロセス間の切り替えはリソースを大量に消費するため、カーネルレベルの介入が必要です。
スレッド: プロセス内の軽量の実行単位であり、プロセスのリソースを共有します。 スレッド切り替えは、プロセス切り替えよりもオーバーヘッドが少なくなります。
コルーチンは独自のレジスタ コンテキストとスタックを維持します。 コルーチン間の切り替えには、この状態の保存と復元が含まれ、中断したところから実行を再開できるようになります。 プロセスやスレッドとは異なり、コルーチン管理はオペレーティング システムではなくユーザー プログラム内で処理されます。ゴルーチンは、特定の種類のコルーチンです。
Go の効率的な同時実行性は、GPM スケジューリング モデルに依存しています。 M、P、G、および Sched の 4 つの主要なコンポーネントが関係します (Sched は図には示されていません)。
M (マシン): カーネルレベルのスレッド。ゴルーチンは
さんで実行されますG (ゴルーチン): 単一のゴルーチン。 各 G には、独自のスタック、命令ポインター、およびその他のスケジューリング関連情報 (待機しているチャネルなど) があります。
P (プロセッサ): ゴルーチンを管理および実行する論理プロセッサ。準備完了の goroutine の実行キューを維持します。
Sched (スケジューラ): M キューと G キューを管理し、効率的なリソース割り当てを保証する中央スケジューラ。
この図は、ゴルーチンを実行するプロセッサ (P) を備えた 2 つの OS スレッド (M) を示しています。
GOMAXPROCS()
は P の数 (つまり、実際の同時実行レベル) を制御します。
灰色の G は準備ができていますが、まだ実行されていません。 P はこの実行キューを管理します。
ゴルーチンを起動すると、P の実行キューに追加されます。
M0 がブロックされている場合、P は M1 (スレッド キャッシュから取得される可能性があります) に切り替わります。
P がタスクを迅速に完了すると、効率を維持するために他の P から仕事を盗む可能性があります。
ゴルーチン実行用の CPU の数を設定します (通常、最近の Go バージョンのデフォルト設定で十分です):
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
<code class="language-go">num := runtime.NumCPU() // Get the number of logical CPUs runtime.GOMAXPROCS(num) // Set the maximum number of concurrently running goroutines</code>
ゴルーチン内の未処理の例外により、プログラム全体が終了する可能性があります。パニックを処理するには、recover()
ステートメント内で defer
を使用します。
<code class="language-go">package main import ( "fmt" "runtime" ) func cal(a, b int) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) for i := 0; i < 10; i++ { go cal(i, i+1) } //Note: The main function exits before goroutines complete in this example. See later sections for synchronization. }</code>
ゴルーチンは非同期で実行されるため、メインプログラムは完了する前に終了する可能性があります。 同期には sync.WaitGroup
またはチャネルを使用します:
sync.WaitGroup
<code class="language-go">package main import ( "fmt" ) func addele(a []int, i int) { defer func() { if r := recover(); r != nil { fmt.Println("Error in addele:", r) } }() a[i] = i // Potential out-of-bounds error if i is too large fmt.Println(a) } func main() { a := make([]int, 4) for i := 0; i < 5; i++ { go addele(a, i) } // ... (add synchronization to wait for goroutines to finish) ... }</code>
<code class="language-go">package main import ( "fmt" "sync" ) func cal(a, b int, wg *sync.WaitGroup) { defer wg.Done() c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go cal(i, i+1, &wg) } wg.Wait() }</code>
チャネルは、ゴルーチン間の通信とデータ共有を容易にします。 グローバル変数も使用できますが、同時実行性の制御を向上させるには、一般にチャネルの使用が推奨されます。
<code class="language-go">package main import ( "fmt" ) func cal(a, b int, ch chan bool) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) ch <- true // Signal completion } func main() { ch := make(chan bool, 10) // Buffered channel to avoid blocking for i := 0; i < 10; i++ { go cal(i, i+1, ch) } for i := 0; i < 10; i++ { <-ch // Wait for each goroutine to finish } }</code>
Leapcell は、Go サービスを展開するための推奨プラットフォームです。
主な機能:
ドキュメントで詳細を確認してください!
リープセル Twitter: https://www.php.cn/link/7884effb9452a6d7a7a79499ef854afd
以上がデコードされた Go の同時実行性: Goroutine のスケジューリングの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。