Avec le développement rapide d'Internet, la demande de systèmes distribués à grande échelle devient de plus en plus élevée, et la programmation simultanée et le calcul parallèle sont devenus des compétences que les développeurs Internet doivent maîtriser. Le langage Go est un langage né pour prendre en charge la concurrence. Il fonctionne très bien en programmation simultanée et en calcul parallèle. Cet article présentera le mode de concurrence et le calcul parallèle du langage Go, et donnera quelques cas pratiques pour aider les lecteurs à comprendre en profondeur.
1. Mode simultanéité du langage Go
Le mode simultanéité du langage Go est principalement basé sur les deux composants de base de la goroutine et du canal. Goroutine est un thread léger, géré par le système d'exécution du langage Go. Il peut être démarré via le mot-clé go et les goroutines peuvent communiquer via des canaux.
Ce qui suit est un exemple simple de goroutine et de canal :
package main import "fmt" func printMsg(msg string, ch chan string) { ch <- msg } func main() { ch := make(chan string) msgs := []string{"Hello", "Golang", "Parallel"} for _, msg := range msgs { go printMsg(msg, ch) } for i := 0; i < len(msgs); i++ { fmt.Println(<-ch) } }
Le code démarre trois goroutines via une boucle for et génère respectivement trois chaînes. La fonction printMsg écrit le message de chaîne dans le canal et la fonction principale lit à nouveau le canal.
1.1 Mode Pipeline
En langage Go, plusieurs goroutines peuvent être connectées en série via le mode pipeline pour former un système concurrent plus complexe. La mise en œuvre du modèle de pipeline se fait généralement via une communication par canal entre plusieurs goroutines, en transmettant des données d'une goroutine à une autre goroutine, et en traitant et en convertissant les données dans chaque goroutine. Voici un exemple simple en mode pipeline :
package main import ( "fmt" ) func addOne(in <-chan int, out chan<- int) { for val := range in { out <- val + 1 } close(out) } func printNums(out <-chan int) { for val := range out { fmt.Println(val) } } func main() { nums := []int{1, 2, 3} in := make(chan int) out := make(chan int) go addOne(in, out) go printNums(out) for _, num := range nums { in <- num } close(in) }
Le code définit 3 goroutines, à savoir la goroutine d'entrée, plus 1 goroutine de traitement et la goroutine de sortie. La fonction addOne ajoute 1 aux données dans le canal d'entrée et les écrit dans le canal de sortie, printNums La fonction lit les données du canal de sortie et les génère.
1.2 Modèle de sélection
L'instruction select du langage Go fournit un moyen pratique de gérer plusieurs canaux, c'est-à-dire le modèle de sélection (modèle de sélection). Le mode de sélection nous permet d'effectuer des opérations de sélection non bloquantes sur plusieurs canaux. Lorsqu'il y a des messages lisibles ou inscriptibles sur plusieurs canaux, un sera automatiquement sélectionné pour l'opération.
Ce qui suit est un exemple simple de mode de sélection :
package main import "fmt" func ping(ch chan<- string) { for { ch <- "ping" } } func pong(ch chan<- string) { for { ch <- "pong" } } func printer(ch <-chan string) { for { fmt.Println(<-ch) } } func main() { ch1 := make(chan string) ch2 := make(chan string) ch3 := make(chan string) go ping(ch1) go pong(ch2) go printer(ch3) for { select { case msg := <-ch1: ch3 <- msg case msg := <-ch2: ch3 <- msg } } }
Dans le code, la fonction ping et la fonction pong envoient des messages "ping" et "pong" respectivement à ch1 et ch2, et la fonction d'imprimante lit le message dans ch3 et sort il. Dans la fonction principale, utilisez l'instruction select pour surveiller les messages dans ch1 et ch2 et transmettez les messages reçus à la fonction d'imprimante via ch3 pour la sortie.
2. Calcul parallèle du langage Go
Les modules de calcul parallèle intégrés du langage Go incluent la synchronisation, l'atomique et le contexte, etc. sync et atomique utilisent principalement le mutex (Mutex) et l'opération atomique (opération atomique) pour contrôler l'accès simultané aux données, et le contexte est utilisé pour gérer les informations contextuelles de goroutine. Voici une brève introduction à l'utilisation de ces modules :
2.1 Verrouillage Mutex
Le verrouillage Mutex est l'un des mécanismes de synchronisation les plus couramment utilisés pour protéger les ressources partagées, et c'est également l'un des mécanismes de synchronisation les plus élémentaires du langage Go. . En langage Go, vous pouvez créer un verrou mutex via le type Mutex dans le package de synchronisation. Le type Mutex propose deux méthodes importantes : Verrouiller et Déverrouiller. Avant d'accéder aux ressources partagées, vous devez appeler la méthode Lock pour obtenir le verrou, puis appeler la méthode Unlock pour libérer le verrou une fois l'accès terminé. Voici un exemple simple de verrouillage mutex :
package main import ( "fmt" "sync" ) func addOne(num *int, mutex *sync.Mutex, wg *sync.WaitGroup) { mutex.Lock() *num += 1 mutex.Unlock() wg.Done() } func main() { var wg sync.WaitGroup var num int mutex := &sync.Mutex{} for i := 0; i < 1000; i++ { wg.Add(1) go addOne(&num, mutex, &wg) } wg.Wait() fmt.Println(num) }
Dans le code, la fonction addOne est définie pour ajouter 1 à la variable num. Le verrou mutex doit être obtenu avant d'ajouter 1, et le verrou mutex doit être libéré après l'ajout. 1. Utilisez WaitGroup pour attendre que toutes les goroutines terminent leur exécution et génèrent les résultats finaux.
2.2 Opérations atomiques
Dans les scénarios de concurrence élevée, les verrous mutex peuvent réduire les performances du programme, c'est pourquoi le langage Go fournit des opérations atomiques pour remplacer les verrous mutex. Le package atomique fournit plusieurs fonctions d'opération atomique, telles que AddInt64, CompareAndSwapInt64, SwapInt64, etc. L'utilisation d'opérations atomiques garantit que les opérations sur les variables ne seront pas interrompues par d'autres goroutines et que l'exécution simultanée ne sera pas affectée. Voici un exemple d'opération atomique simple :
package main import ( "fmt" "sync/atomic" ) func addOne(num *int64, count *int64, done chan bool) { for i := 0; i < 1000; i++ { atomic.AddInt64(num, 1) } atomic.AddInt64(count, 1) done <- true } func main() { var num int64 var count int64 done := make(chan bool) for i := 0; i < 100; i++ { go addOne(&num, &count, done) } for i := 0; i < 100; i++ { <-done } fmt.Printf("num=%d, count=%d ", num, count) }
Dans le code, la fonction AddInt64 du package atomique est utilisée pour effectuer des opérations atomiques sur la variable num Une fois l'opération terminée, le thread principal est notifié via done. La variable count est accumulée via la fonction AddInt64 ordinaire, et les valeurs de num et count sont finalement sorties.
2.3 Gestion du contexte
Dans le langage Go, il est souvent nécessaire de transmettre des informations de contexte entre plusieurs goroutines, telles que l'ID de demande, les paramètres de délai d'attente, etc. Le package context fournit un moyen pratique de gérer les informations contextuelles des goroutines. Lorsque vous utilisez le contexte, vous devez généralement créer un contexte parent dans la goroutine principale. Lors de la dérivation de la goroutine, utilisez WithCancel, WithDeadline, WithValue et d'autres fonctions pour créer un contexte enfant et transmettre les informations de contexte correspondantes. Voici un exemple simple de gestion de contexte :
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("worker %d canceled ", id) return default: fmt.Printf("worker %d is working ", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) for i := 0; i < 3; i++ { go worker(ctx, i) } time.Sleep(5 * time.Second) cancel() }
Dans le code, utilisez le package de contexte pour créer un contexte parent et créez un contexte enfant via la fonction WithCancel. Dans la fonction de travail, utilisez l'instruction select pour écouter le signal de ctx.Done(). Lorsque ctx.Done() est fermé, cela signifie que le contexte est annulé et que la fonction de travail doit se terminer. Dans la fonction principale, fermez le sous-contexte via la fonction d'annulation et attendez que le sous-contexte soit annulé. Les résultats d'exécution sont les suivants :
worker 0 is working worker 1 is working worker 2 is working worker 2 canceled worker 1 canceled worker 0 canceled
Lorsque le contexte parent est annulé, tous les contextes enfants recevront des notifications et quitteront l'exécution.
3.Conclusion
Cet article présente brièvement le mode de concurrence et le calcul parallèle du langage Go, et présente les composants et modules de base tels que la goroutine, le canal, le verrouillage mutex, le fonctionnement atomique et le contexte. En apprenant ces connaissances de base, nous pouvons mieux maîtriser la concurrence et la programmation parallèle du langage Go et jeter les bases de la création d'applications Internet hautes performances et à haute concurrence.
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!