Files d'attente de tâches en Go : guide complet pour débutants

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction aux files d'attente de tâches en Go

\n

Les files d'attente de tâches (task queues) sont l'un des modèles clés dans le développement d'applications à forte charge. Elles permettent d'organiser le traitement asynchrone des données, de répartir la charge et d'améliorer la tolérance aux pannes du système. En langage Go, grâce aux mécanismes de concurrence intégrés — goroutines et canaux — l'implémentation des files d'attente devient incroyablement élégante et performante.

\n

Dans cet article, nous allons voir comment créer des files d'attente de tâches en Go à partir de zéro. Vous apprendrez ce que sont les goroutines et les canaux, comment organiser un pool de workers (worker pool) et traiter les tâches en parallèle. Le contenu est destiné aux développeurs débutants familiers avec les bases de la syntaxe Go.

\n\n

Les bases : goroutines et canaux

\n

Avant de passer aux files d'attente, il est important de comprendre deux concepts fondamentaux de Go :

\n
    \n
  • Goroutine — un fil d'exécution léger. Il est lancé avec le mot-clé go avant l'appel d'une fonction.
  • \n
  • Canal (channel) — un « canal » typé pour transférer des données entre goroutines. Il permet de synchroniser leur travail.
  • \n
\n

Exemple d'une goroutine et d'un canal simples :

\n
package main\n\nimport "fmt"\n\nfunc main() {\n    ch := make(chan string)\n\n    go func() {\n        ch <- "Bonjour depuis la goroutine !"\n    }()\n\n    msg := <-ch\n    fmt.Println(msg)\n}\n
\n

Dans cet exemple, nous avons créé un canal ch, lancé une goroutine anonyme qui envoie un message dans ce canal, et la goroutine principale a reçu ce message. C'est sur ce mécanisme que reposent les files d'attente de tâches.

\n\n

Implémentation d'une file d'attente de tâches simple

\n

La file d'attente de tâches la plus simple est un canal dans lequel nous plaçons des tâches, et une ou plusieurs goroutines qui les traitent. Regardons un exemple de base :

\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc worker(id int, tasks <-chan int) {\n    for task := range tasks {\n        fmt.Printf("Worker %d a commencé la tâche %d\\n", id, task)\n        time.Sleep(time.Second) // simulation de travail\n        fmt.Printf("Worker %d a terminé la tâche %d\\n", id, task)\n    }\n}\n\nfunc main() {\n    tasks := make(chan int, 10) // canal tamponné\n\n    // lancement d'un worker\n    go worker(1, tasks)\n\n    // envoi de 5 tâches\n    for i := 1; i <= 5; i++ {\n        tasks <- i\n    }\n\n    close(tasks) // fermeture du canal\n    time.Sleep(2 * time.Second) // laisser le temps de traiter\n}\n
\n

Ici, worker est une fonction qui lit depuis le canal tasks. Le canal est tamponné (capacité 10), donc nous pouvons envoyer plusieurs tâches sans blocage. Après la fermeture du canal, la boucle for range se termine automatiquement.

\n

Notez : nous avons utilisé <-chan int — un canal en lecture seule. C'est une bonne pratique qui indique clairement que le worker ne doit pas envoyer de données dans ce canal.

\n\n

Canaux tamponnés et non tamponnés

\n

Dans l'exemple ci-dessus, nous avons utilisé un canal tamponné (make(chan int, 10)). La différence entre les types de canaux :

\n
    \n
  • Canal non tamponné — l'envoi est bloqué jusqu'à ce que le récepteur lise les données. Idéal pour la synchronisation.
  • \n
  • Canal tamponné — l'envoi est bloqué uniquement lorsque le tampon est plein. Permet d'accumuler les tâches et de réduire les latences.
  • \n
\n

Pour les files d'attente de tâches, on utilise généralement des canaux tamponnés afin que le producteur puisse envoyer rapidement une tâche et continuer son travail sans attendre son exécution.

\n\n

Pool de workers (Worker Pool) en Go

\n

Un worker, c'est bien, mais plusieurs, c'est mieux. Un pool de workers permet de traiter les tâches en parallèle, en utilisant tous les cœurs du processeur. Voici comment l'implémenter :

\n
package main\n\nimport (\n    "fmt"\n    "sync"\n    "time"\n)\n\nfunc worker(id int, tasks <-chan int, wg *sync.WaitGroup) {\n    defer wg.Done()\n    fo                

Blogs

Book Recommendations