この投稿では、ゴルーチンとチャネルについて紹介します。これらは Go で最も便利な 2 つの構造です。これらを適切に使用すると、開発者は同時実行性を非常に柔軟に処理できます。それらはインタビューで最も一般的なトピックの 1 つです。
シンプルなプロデューサー コンシューマー パターンを Go に実装します。
var buffer = make(chan int, 5) func produce(wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 10; i++ { buffer <- i time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) } fmt.Println("producer done") } func consume(wg *sync.WaitGroup) { defer wg.Done() for data := range buffer { fmt.Println(data) time.Sleep(time.Millisecond * time.Duration(rand.Intn(400))) } fmt.Println("consumer done") } func main() { var producerWg sync.WaitGroup var consumerWg sync.WaitGroup producerWg.Add(1) go produce(&producerWg) go func() { producerWg.Wait() close(buffer) fmt.Println("closed channel") }() consumerWg.Add(1) go consume(&consumerWg) consumerWg.Wait() fmt.Println("done") }
これは最も単純な実装の 1 つです。しかし、このパターンは非常に一般的です。値を「生成」するスレッドと、値を「消費」する必要があるスレッドがあります。 golang では、スレッド間でこれらの値を渡す方法はチャネルです。
まず、整数用のチャネルを作成します。次に、プロデューサー関数とコンシューマー関数を実装するルーチンを作成します。
マルチスレッドの状況では、同期が問題になります。 Golang は、同期を実装する手段の 1 つとして WaitGroup を作成しました。これらは単にカウンターとして機能し、同期する必要があるスレッドはカウントが 0 になるまで待機します。制御スレッドは Done() 関数を使用してカウンターをデクリメントします。
この問題では、プロデューサーとコンシューマーの両方に WaitGroup を作成し、両方をカウント 1 に初期化します (Add() 関数を使用)。
メイン スレッドはプロデューサー、コンシューマー、およびプロデューサーを待機するインライン スレッドを起動し、その後コンシューマーが完了するのを待ちます。
プロデューサー スレッドは通常どおりデータの送信を開始します。完了すると、WaitGroup を使用して、チャネルへの送信が完了したことを通知します。インライン goroutine は、チャネルを閉じるプロデューサー WaitGroup を待機します。チャネルが閉じられない場合、コンシューマはさらなるデータを待って永遠にスリープ状態になり、プロセスは決して終了しません。
コンシューマにデータがなくなると (チャネルが閉じられたため)、2 番目の WaitGroup に完了を通知します。
プロデューサー スレッドとコンシューマー スレッドを起動したメイン スレッドは、コンシューマー WaitGroup が完了を許可するまで待機します。これにより、メインスレッドが途中で終了し、プロセス内のすべてのスレッドが強制終了されるのを防ぎます。
これは、生産者と消費者のパターンを実装する唯一の方法ではありません。
SIGTERM や SIGINT などのシグナルからの外部終了など、製品コードで対処する必要がある問題もいくつかあります。これは基本を示す簡単なデモンストレーションです。
他にどのように実装しますか?上記の実装には何が欠けていますか?コメントや他の実装へのリンクを以下に投稿してください。
ありがとうございます!
この投稿とこのシリーズのすべての投稿のコードはここにあります
以上が生産者と消費者のパターンの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。