Maison >développement back-end >Golang >Concurrence dans Go : des bases aux concepts avancés

Concurrence dans Go : des bases aux concepts avancés

Linda Hamilton
Linda Hamiltonoriginal
2024-10-03 06:11:30438parcourir

Concurrency in Go: From Basics to Advanced Concepts

Table des matières

  1. Introduction à la concurrence
  2. Concurrence vs Parallélisme
  3. Go-routines : les éléments constitutifs de la concurrence
  4. Canaux : communication entre les routines Go
  5. Déclaration de sélection : gestion de plusieurs canaux
  6. Primitives de synchronisation
  7. Modèles de concurrence
  8. Pack Context : Gestion des annulations et Délais d'attente.
  9. Meilleures pratiques et pièges courants**

1.Introduction à la concurrence

La concurrence est la capacité de gérer plusieurs tâches simultanément. Dans Go, la concurrence est un citoyen de premier ordre, intégré à la conception de base du langage. L'approche de Go en matière de concurrence est basée sur les processus séquentiels communicants (CSP), un modèle qui met l'accent sur la communication entre les processus plutôt que sur la mémoire partagée.

2.Concurrence vs Parallélisme :

Les Go-routines activent la concurrence, qui est la composition de processus s'exécutant indépendamment.
Le parallélisme (exécution simultanée) peut se produire si le système dispose de plusieurs cœurs de processeur et que le runtime Go planifie les go-routines pour qu'elles s'exécutent en parallèle.

3. Go-routines :
Les éléments constitutifs de la concurrence sont que les routines Go sont des threads légers gérés par le runtime Go. C'est une fonction ou une méthode qui s'exécute simultanément avec d'autres fonctions ou méthodes. Les routines Go sont le fondement du modèle de concurrence de Go.

Caractéristiques clés :

  • Léger : les routines Go sont beaucoup plus légères que les threads du système d'exploitation. Vous pouvez facilement créer des milliers de routines sans impact significatif sur les performances.
  • Géré par le runtime Go : le planificateur Go gère la distribution des routines go entre les threads du système d'exploitation disponibles.
  • Création bon marché : démarrer une routine go est aussi simple que d'utiliser le mot-clé go avant un appel de fonction.
  • Taille de la pile : les routines Go commencent par une petite pile (environ 2 Ko) qui peut s'agrandir et se réduire selon les besoins.

Créer une routine Go :
Pour démarrer une go-routine, vous utilisez simplement le mot-clé go suivi d'un appel de fonction :

go functionName()

Ou avec une fonction anonyme :

go func() {
    // function body
}()

Planification des routines :

  • Le runtime Go utilise un planificateur M:N, où M go-routines sont planifiées sur des threads N OS.
  • Ce planificateur est non préemptif, ce qui signifie que les routines de démarrage cèdent le contrôle lorsqu'elles sont inactives ou logiquement bloquées.

Communication et synchronisation :

  • Les Goroutines communiquent généralement à l'aide de canaux, adhérant au principe « Ne communiquez pas en partageant la mémoire ; partagez la mémoire en communiquant ».
  • Pour une synchronisation simple, vous pouvez utiliser des primitives comme sync.WaitGroup ou sync.Mutex.

Exemple avec explication :

package main

import (
    "fmt"
    "time"
)

func printNumbers() {
    for i := 1; i <= 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Printf("%d ", i)
    }
}

func printLetters() {
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(150 * time.Millisecond)
        fmt.Printf("%c ", i)
    }
}

func main() {
    go printNumbers()
    go printLetters()
    time.Sleep(2 * time.Second)
    fmt.Println("\nMain function finished")
}

Explication :

  • Nous définissons deux fonctions : printNumbers et printLetters.
  • Dans l'ensemble, nous démarrons ces fonctions en tant que goroutines en utilisant le mot-clé go.
  • La fonction principale se met ensuite en veille pendant 2 secondes pour permettre aux goroutines de se terminer.
  • Sans goroutines, ces fonctions s'exécuteraient de manière séquentielle. Avec les goroutines, ils s'exécutent simultanément.
  • La sortie affichera des chiffres et des lettres entrelacés, démontrant une exécution simultanée.

Cycle de vie des goroutines :

  • Une goroutine démarre lorsqu'elle est créée avec le mot-clé go.
  • Il se termine lorsque sa fonction est terminée ou lorsque le programme se termine.
  • Les goroutines peuvent fuir si elles ne sont pas correctement gérées, il est donc important de s'assurer qu'elles peuvent sortir.

Bonnes pratiques :

  • Ne créez pas de goroutines dans les bibliothèques ; laissez l'appelant contrôler la simultanéité.
  • Soyez prudent lorsque vous créez un nombre illimité de goroutines.
  • Utilisez des canaux ou des primitives de synchronisation pour coordonner les goroutines.
  • Envisagez d'utiliser des pools de tâches pour gérer efficacement plusieurs goroutines.

Exemple simple avec explications des go-routines

package main

import (
    "fmt"
    "time"
)

// printNumbers is a function that prints numbers from 1 to 5
// It will be run as a goroutine
func printNumbers() {
    for i := 1; i <= 5; i++ {
        time.Sleep(500 * time.Millisecond) // Sleep for 500ms to simulate work
        fmt.Printf("%d ", i)
    }
}

// printLetters is a function that prints letters from 'a' to 'e'
// It will also be run as a goroutine
func printLetters() {
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(300 * time.Millisecond) // Sleep for 300ms to simulate work
        fmt.Printf("%c ", i)
    }
}

func main() {
    // Start printNumbers as a goroutine
    // The 'go' keyword before the function call creates a new goroutine
    go printNumbers()

    // Start printLetters as another goroutine
    go printLetters()

    // Sleep for 3 seconds to allow goroutines to finish
    // This is a simple way to wait, but not ideal for production code
    time.Sleep(3 * time.Second)

    // Print a newline for better formatting
    fmt.Println("\nMain function finished")
}

4.Canaux :

Les canaux sont une fonctionnalité essentielle de Go qui permettent aux go-routines de communiquer entre elles et de synchroniser leur exécution. Ils permettent à une routine go d'envoyer des données à une autre routine go.

Objectif des chaînes

Les chaînes dans Go ont deux objectifs principaux :
a) Communication : ils permettent aux goroutines d'envoyer et de recevoir des valeurs entre elles.
b) Synchronisation : ils peuvent être utilisés pour synchroniser l'exécution entre les goroutines.

Création : Les chaînes sont créées à l'aide de la fonction make :

ch := make(chan int)  // Unbuffered channel of integers

Envoi : les valeurs sont envoyées à un canal à l'aide de l'opérateur <- :

ch <- 42  // Send the value 42 to the channel

Receiving: Values are received from a channel using the <- operator:

value := <-ch  // Receive a value from the channel

Types of Channels

a) Unbuffered Channels:

  • Created without a capacity: ch := make(chan int)
  • Sending blocks until another goroutine receives.
  • Receiving blocks until another goroutine sends.
ch := make(chan int)
go func() {
    ch <- 42  // This will block until the value is received
}()
value := <-ch  // This will receive the value

b) Buffered Channels:

  • Created with a capacity: ch := make(chan int, 3)
  • Sending only blocks when the buffer is full.
  • Receiving only blocks when the buffer is empty.
ch := make(chan int, 2)
ch <- 1  // Doesn't block
ch <- 2  // Doesn't block
ch <- 3  // This will block until a value is received

Channel Directions

Channels can be directional or bidirectional:

  • Bidirectional: chan T
  • Send-only: chan<- T
  • Receive-only: <-chan T

Example :

func send(ch chan<- int) {
    ch <- 42
}

func receive(ch <-chan int) {
    value := <-ch
    fmt.Println(value)
}

Closing Channels

Channels can be closed to signal that no more values will be sent:

close(ch)

Receiving from a closed channel:

If the channel is empty, it returns the zero value of the channel's type.
You can check if a channel is closed using a two-value receive:

value, ok := <-ch
if !ok {
    fmt.Println("Channel is closed")
}

Ranging over Channels

You can use a for range loop to receive values from a channel until it's closed:

for value := range ch {
    fmt.Println(value)
}

Hey, Thank you for staying until the end! I appreciate you being valuable reader and learner. Please follow me here and also on my Linkedin and GitHub .

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn