GoroutineとチャネルでGoにおける並行処理をマスターする
Min-jun Kim
Dev Intern · Leapcell

Key Takeaways
- Goroutinesは、Goで効率的な並行実行を可能にする軽量スレッドです。
- チャネルは、goroutine間の安全な通信と同期を容易にします。
- ワーカープールやパイプラインのようなパターンは、並行処理を効果的に構造化し、管理するのに役立ちます。
Go(しばしばGolangと呼ばれる)は、並行処理の組み込みサポートで有名であり、効率的でスケーラブルなアプリケーションを開発するための強力なツールとなっています。Goの並行処理モデルの中心となるのは、goroutineとチャネルであり、これらは複数のタスクを同時に実行し、タスク間の通信を可能にします。
Goroutines: 軽量スレッド
goroutineは、Goランタイムによって管理される軽量スレッドです。従来のスレッドとは異なり、goroutineはメモリ効率が高く、より少ない数のOSスレッドに多重化されるため、大きなオーバーヘッドなしに何千もの同時実行タスクを作成できます。
goroutineを開始するには、関数呼び出しにgo
キーワードを付けるだけです。
package main import ( "fmt" "time" ) func sayHello() { fmt.Println("goroutineからのHello") } func main() { go sayHello() time.Sleep(1 * time.Second) // goroutineが実行を完了する時間を与える fmt.Println("Main function finished") }
この例では、sayHello
はmain
関数と並行して実行されます。time.Sleep
は、goroutineが実行を完了する前にプログラムが終了しないようにします。
チャネル: Goroutine間の通信
Goのチャネルは、goroutineが通信し、その実行を同期する方法を提供します。これらは、チャネル演算子<-
を使用して値を送受信できる型付きパイプです。
チャネルの作成と使用
チャネルを作成して使用する方法を次に示します。
package main import "fmt" func main() { messages := make(chan string) go func() { messages <- "こんにちは、チャネル!" }() msg := <-messages fmt.Println(msg) }
このコードでは:
make(chan string)
は、string
型の新しいチャネルを作成します。- 匿名goroutineは、チャネルにメッセージを送信します。
- メイン関数は、チャネルからメッセージを受信して出力します。
バッファ付きチャネルとバッファなしチャネル
-
バッファなしチャネル: これらのチャネルは、別のgoroutineがチャネルから値を受信するまで、送信goroutineをブロックします。これらは同期に役立ちます。
-
バッファ付きチャネル: これらのチャネルには容量があり、対応する受信なしに制限された数の値を送信できます。これらは、送信者と受信者の間のタイミングを分離する場合に役立ちます。
バッファ付きチャネルの例:
package main import "fmt" func main() { messages := make(chan string, 2) messages <- "Buffered" messages <- "Channel" fmt.Println(<-messages) fmt.Println(<-messages) }
Goの並行処理パターン
Goの並行処理モデルにより、さまざまな並行処理パターンを実装できます。一般的なものをいくつか示します。
ワーカープールパターン
ワーカープールパターンには、共有チャネルからタスクを処理する固定数のgoroutine(ワーカー)を作成することが含まれます。このパターンは、並行処理レベルを制御し、システムリソースを効率的に利用するのに役立ちます。
package main import ( "fmt" "sync" ) func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) { defer wg.Done() for j := range jobs { fmt.Printf("Worker %d processing job %d\n", id, j) results <- j * 2 } } func main() { const numJobs = 5 jobs := make(chan int, numJobs) results := make(chan int, numJobs) var wg sync.WaitGroup for w := 1; w <= 3; w++ { wg.Add(1) go worker(w, jobs, results, &wg) } for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) wg.Wait() close(results) for r := range results { fmt.Println("Result:", r) } }
この例では:
- 3人のワーカーが5つのジョブを処理します。
sync.WaitGroup
は、メイン関数がすべてのワーカーが終了するまで待機するようにします。
パイプラインパターン
パイプラインパターンには、一連のステージをチェーンすることが含まれます。各ステージは、入力チャネルを受け取り、出力チャネルを返す関数です。このパターンは、複数のステップでデータを並行して処理するのに役立ちます。
package main import "fmt" func gen(nums ...int) <-chan int { out := make(chan int) go func() { for _, n := range nums { out <- n } close(out) }() return out } func sq(in <-chan int) <-chan int { out := make(chan int) go func() { for n := range in { out <- n * n } close(out) }() return out } func main() { c := gen(2, 3, 4) out := sq(c) for n := range out { fmt.Println(n) } }
このパイプラインでは:
gen
は数値を生成し、チャネルに送信します。sq
はチャネルから数値を読み取り、それらを2乗し、結果を別のチャネルに送信します。
Goでの並行処理のベストプラクティス
-
競合状態の回避: チャネルまたは
sync.Mutex
のような同期プリミティブを使用して、共有変数への同時アクセスを防ぎます。 -
Goroutineの制限: goroutineを制御せずに生成すると、リソースが枯渇する可能性があります。ワーカープールまたはセマフォを使用して並行処理を制限します。
-
正常なシャットダウン: コンテキストキャンセルまたはチャネルのクローズを使用して、goroutineに停止するように通知します。
-
エラー処理: エラーを適切に処理し、それらをメイン関数またはエラー処理ルーチンに通知するようにgoroutineを設計します。
結論
Goの並行処理モデルは、goroutineとチャネルを中心に構築されており、並行アプリケーションを構築するための堅牢なフレームワークを提供します。これらの構成を活用し、確立された並行処理パターンに従うことで、開発者は最新のマルチコアプロセッサを最大限に活用する効率的でスケーラブル、保守性の高いコードを作成できます。
詳細な情報と実践的な例については、次のリソースを参照してください。
FAQs
バッファなしチャネルは受信されるまでブロックしますが、バッファ付きチャネルは制限された非同期送信を許可します。
goroutineはGoランタイムによって管理され、より軽量であるため、何千ものgoroutineを同時に実行できます。
コンテキストキャンセルを使用するか、チャネルを閉じて、正常なシャットダウンのためにgoroutineに通知します。
Goプロジェクトのホスティングに最適なLeapcellをご紹介します。
Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです。
マルチ言語サポート
- Node.js、Python、Go、またはRustで開発します。
無制限のプロジェクトを無料でデプロイする
- リクエストや料金は発生せず、使用量に対してのみお支払いいただきます。
比類のない費用対効果
- アイドル料金なしの従量課金制。
- 例:25ドルで、平均応答時間60ミリ秒で694万件のリクエストをサポートします。
合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOpsの統合。
- 実用的な洞察を得るためのリアルタイムのメトリクスとロギング。
簡単なスケーラビリティと高性能
- 高い同時実行性を容易に処理するための自動スケーリング。
- 運用上のオーバーヘッドはゼロ — 構築に集中するだけです。
詳細については、ドキュメントをご覧ください。
Xでフォローしてください: @LeapcellHQ