1.すべての例を実行: コードを読むだけではありません。入力して実行し、動作を観察してください。⚠️ このシリーズをどのように進めていきますか?
2.実験と破壊: スリープを削除して何が起こるか確認し、チャネル バッファー サイズを変更し、ゴルーチン数を変更します。
物を壊すことで、その仕組みがわかる
3.動作に関する理由: 変更されたコードを実行する前に、結果を予測してみてください。予期せぬ動作が見られた場合は、立ち止まってその理由を考えてください。解説に挑戦してください。
4.メンタル モデルの構築: 各ビジュアライゼーションはコンセプトを表します。変更されたコード用に独自の図を描いてみてください。
前回の投稿では、Go の並行性の構成要素であるゴルーチンとチャネルの基本について説明しました。ここをお読みください:
ここで、これらのプリミティブがどのように組み合わされて、現実世界の問題を解決する強力なパターンを形成するかを見てみましょう。
この投稿では、ジェネレーター パターン について説明し、視覚化してみます。プロセス全体を通じて実際に作業を進めていきますので、準備を整えましょう。
ジェネレーターは、必要なときにいつでも消費できる価値を継続的に生成する噴水のようなものです。
Go では、これは値のストリームを生成し、チャネルを通じて送信する関数であり、プログラムの他の部分がこれらの値をオンデマンドで受信できるようにします。
例を見てみましょう:
// generateNumbers creates a generator that produces numbers from 1 to max func generateNumbers(max int) chan int { // Create a channel to send numbers out := make(chan int) // Launch a goroutine to generate numbers go func() { // Important: Always close the channel when done defer close(out) for i := 1; i <= max; i++ { out <- i // Send number to channel } }() // Return channel immediately return out } // Using the generator func main() { // Create a generator that produces numbers 1-5 numbers := generateNumbers(5) // Receive values from the generator for num := range numbers { fmt.Println("Received:", num) } }
この例では、ジェネレーター関数は 3 つの重要なことを実行します。
大きなファイルを 1 行ずつ読み取る:
func generateLines(filename string) chan string { out := make(chan string) go func() { defer close(out) file, err := os.Open(filename) if err != nil { return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { out <- scanner.Text() } }() return out }
ここで、何がそんなに特別なの?と考えているかもしれません。データのシーケンスを生成したり、ゴルーチン を使用せずに 1 行ずつ読み取るのと同じことを行うことができます。やりすぎじゃないですか?両方のケースを視覚化してみましょう:
ゴルーチンなし
// Traditional approach func getNumbers(max int) []int { numbers := make([]int, max) for i := 1; i <= max; i++ { numbers[i-1] = i // Imagine some heavy computation here time.Sleep(100 * time.Millisecond) } return numbers }
ここでは、処理を開始する前に、すべての準備が整うまで待つ必要があります。
ゴルーチンを使用する
// Generator approach func generateNumbers(max int) chan int { out := make(chan int) go func() { defer close(out) for i := 1; i <= max; i++ { out <- i // Same heavy computation time.Sleep(100 * time.Millisecond) } }() return out }
データの生成中にデータの処理を開始できます。
ノンブロッキング実行: 生成と処理は同時に行われます
メモリ効率: 一度に 1 つの値を生成して処理できるため、すぐにメモリに保存する必要はありません
無限シーケンス: メモリの問題なく無限シーケンスを生成できます
バックプレッシャー処理: コンシューマが遅い場合、ジェネレータは自然に (チャネル ブロックにより) 速度を落とし、メモリの過負荷を防ぎます。
// generateNumbers creates a generator that produces numbers from 1 to max func generateNumbers(max int) chan int { // Create a channel to send numbers out := make(chan int) // Launch a goroutine to generate numbers go func() { // Important: Always close the channel when done defer close(out) for i := 1; i <= max; i++ { out <- i // Send number to channel } }() // Return channel immediately return out } // Using the generator func main() { // Create a generator that produces numbers 1-5 numbers := generateNumbers(5) // Receive values from the generator for num := range numbers { fmt.Println("Received:", num) } }
func generateLines(filename string) chan string { out := make(chan string) go func() { defer close(out) file, err := os.Open(filename) if err != nil { return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { out <- scanner.Text() } }() return out }
// Traditional approach func getNumbers(max int) []int { numbers := make([]int, max) for i := 1; i <= max; i++ { numbers[i-1] = i // Imagine some heavy computation here time.Sleep(100 * time.Millisecond) } return numbers }
// Generator approach func generateNumbers(max int) chan int { out := make(chan int) go func() { defer close(out) for i := 1; i <= max; i++ { out <- i // Same heavy computation time.Sleep(100 * time.Millisecond) } }() return out }
ジェネレータ パターンについては以上です。次は パイプライン同時実行パターン です。 Golang の同時実行性に関する概念を明確にするために、ご期待ください。
何か見逃したでしょうか?質問がありますか?何か面白いことを共有したいですか?すべてのコメントを歓迎します。
以上がGo のジェネレーター同時実行パターン: 包括的なガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。