同時プログラミングでは、同期はデータ競合を防止し、スレッドまたはゴルーチンが調整された方法で動作するようにするための鍵となります。 バッファやキューなどの共有リソースにアクセスする複数のプロデューサーとコンシューマーを調整するという問題があると想像してください。この古典的な同時実行性の課題は、プロデューサーとコンシューマーの問題として知られています。このシナリオでは、プロデューサーがデータを上書きしないように、またコンシューマーが無効または古いデータを読み取らないようにするために、同期が不可欠です。適切な同期がないと、共有データへの同時アクセスが競合状態、データの破損、またはクラッシュにつながる可能性があるため、同期が必要です。バッファーがいっぱいの場合はプロデューサーは待機する必要があり、バッファーが空の場合はコンシューマーは待機する必要があります。 固定サイズの境界付きバッファーがあり、複数のプロデューサーとコンシューマーの間でそのバッファーへのアクセスを管理する必要があるシナリオが考えられます。
sync.Cond とは何ですか?
バッファ (またはキュー) があると想像してください。複数のプロデューサーが項目を生成してバッファーに追加し、複数のコンシューマーがバッファーから項目を削除します。課題は次のとおりです:
初期のコード構造は次のとおりです:
package main import ( "fmt" "sync" "time" ) const bufferSize = 5 type Buffer struct { data []int mu sync.Mutex cond *sync.Cond } func (b *Buffer) produce(item int) { // Producer logic to add item to the buffer } func (b *Buffer) consume() int { // Consumer logic to remove item from the buffer return 0 } func main() { buffer := &Buffer{data: make([]int, 0, bufferSize)} buffer.cond = sync.NewCond(&buffer.mu) var wg sync.WaitGroup // Start producer goroutines for i := 1; i <= 3; i++ { wg.Add(1) go func(id int) { defer wg.Done() for j := 0; j < 5; j++ { // Each producer creates 5 items buffer.produce(id*10 + j) // Produce unique items based on id and j time.Sleep(100 * time.Millisecond) } }(i) } // Start consumer goroutines for i := 1; i <= 3; i++ { wg.Add(1) go func(id int) { defer wg.Done() for j := 0; j < 5; j++ { // Each consumer consumes 5 items item := buffer.consume() fmt.Printf("Consumer %d consumed item %d\n", id, item) time.Sleep(150 * time.Millisecond) } }(i) } wg.Wait() fmt.Println("All producers and consumers finished.") }
エンジニアとしての私たちの仕事は、これらの要件を達成するために生産および消費の方法を実装することです。プロデュース メソッドはアイテムをバッファに追加し、アイテムが追加されたときに消費者に通知します。 consume メソッドはバッファからアイテムを削除し、アイテムが削除されたときにプロデューサーに通知します。この問題は、sync.Cond を使用して待機し、バッファーがいっぱいまたは空になったときに通知することでシームレスに解決できます。
生成メソッドと消費メソッドで sync.Cond がどのように使用されるかを詳しく説明します。
初期化:
buffer.cond = sync.NewCond(&buffer.mu)
プロデューサーメソッド (プロデュース):
func (b *Buffer) produce(item int) { b.mu.Lock() defer b.mu.Unlock() // Wait if the buffer is full for len(b.data) == bufferSize { b.cond.Wait() // Release lock and wait until signaled } // Add item to the buffer b.data = append(b.data, item) fmt.Printf("Produced item %d\n", item) // Signal a consumer that an item is available b.cond.Signal() }
コンシューマ メソッド (consume):
func (b *Buffer) consume() int { b.mu.Lock() defer b.mu.Unlock() // Wait if the buffer is empty for len(b.data) == 0 { b.cond.Wait() // Release lock and wait until signaled } // Remove item from the buffer item := b.data[0] b.data = b.data[1:] fmt.Printf("Consumed item %d\n", item) // Signal a producer that space is available b.cond.Signal() return item }
この例では:
この調整により、プロデューサーとコンシューマーは干渉やデッドロックなしでバッファを共有し、バッファの状態に基づいてアクセスを効率的に管理できます。
複数のゴルーチンが続行する前に特定の条件を待機する必要があるタスクがあるとします。
以上がGo の sync.Cond について: プロデューサー / コンシューマー シナリオでのゴルーチンの同期の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。