Maison > Article > développement back-end > Comprendre sync.Cond in Go : synchronisation des goroutines dans les scénarios producteur-consommateur
En programmation simultanée, la synchronisation est essentielle pour éviter les courses de données et garantir que les threads ou les goroutines fonctionnent de manière coordonnée. Imaginez que vous ayez un problème pour coordonner plusieurs producteurs et consommateurs accédant à une ressource partagée, telle qu'un tampon ou une file d'attente. Ce défi de concurrence classique est connu sous le nom de problème producteur-consommateur. Dans ce scénario, la synchronisation est essentielle pour garantir que les producteurs n’écrasent pas les données et que les consommateurs ne lisent pas de données invalides ou périmées. La synchronisation est nécessaire, car sans synchronisation appropriée, l'accès simultané aux données partagées peut entraîner des conditions de concurrence critique, une corruption des données ou des pannes. Les producteurs doivent attendre si le tampon est plein, et les consommateurs doivent attendre si le tampon est vide. Il peut y avoir des scénarios dans lesquels vous disposez d'un tampon limité avec une taille fixe et vous devez en gérer l'accès entre plusieurs producteurs et consommateurs.
sync.Cond in Go est un mécanisme de signalisation qui permet aux goroutines d'attendre qu'une condition spécifique soit remplie. Il est particulièrement utile pour coordonner des flux de travail complexes où certaines goroutines doivent suspendre l'exécution et attendre que d'autres goroutines terminent certaines actions. Les idées derrière sync.Cond sont assez simples et faciles à comprendre :
Imaginez que vous ayez un tampon (ou une file d'attente) de taille fixe. Plusieurs producteurs génèrent des éléments et les ajoutent au tampon, tandis que plusieurs consommateurs en suppriment des éléments. Le défi est de :
Voici la structure initiale du code :
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.") }
Notre tâche, en tant qu'ingénieur, est de mettre en œuvre des méthodes de production et de consommation pour répondre à ces exigences. La méthode product ajoute des éléments au tampon et informe les consommateurs lorsqu'un élément est ajouté. La méthode de consommation supprime les éléments du tampon et informe les producteurs lorsqu'un élément est supprimé. Ce problème peut être résolu de manière transparente en utilisant sync.Cond pour attendre et signaler lorsque le tampon est plein ou vide.
Voici un aperçu de la façon dont sync.Cond est utilisé dans les méthodes de production et de consommation :
Initialisation :
buffer.cond = sync.NewCond(&buffer.mu)
Méthode Producteur (produire) :
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() }
Méthode du consommateur (consommer) :
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 }
Dans cet exemple :
Cette coordination permet aux producteurs et aux consommateurs de partager le tampon sans interférence ni blocage, en gérant efficacement l'accès en fonction de l'état du tampon.
Imaginez que vous ayez des tâches pour lesquelles plusieurs goroutines doivent attendre une condition spécifique avant de continuer, telles que :
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!