Erreur N1 : Deadlock
Le problème
Un deadlock est une situation où deux ou plusieurs goroutines s'attendent mutuellement sur des conditions qui ne peuvent jamais être remplies. Cela peut entraîner une application qui se bloque indéfiniment.
package main
import (
"fmt"
"time"
)
func worker(id int, ch chan bool) {
for {
select {
case <-ch:
fmt.Println("Worker", id, "is working")
time.Sleep(time.Second)
ch <- true // Deadlock here
}
}
}
func main() {
ch := make(chan bool)
for i := 1; i <= 5; i++ {
go worker(i, ch)
}
<-ch // This line is never reached, causing a deadlock
}
Pourquoi c'est une erreur
Un deadlock peut entraîner une perte de performance significative car il empêche les autres goroutines d'avancer. En outre, cela rend l'application imprévisible et difficile à déboguer.
La solution
Pour éviter un deadlock, vous devez vous assurer que chaque case case dans un select est couverte par une autre case ou un default. Dans ce cas, nous devons ajouter une case default pour permettre au programme de continuer même sans reçu.
package main
import (
"fmt"
"time"
)
func worker(id int, ch chan bool) {
for {
select {
case <-ch:
fmt.Println("Worker", id, "is working")
time.Sleep(time.Second)
ch <- true // Fixed deadlock here
default:
fmt.Println("Worker", id, "waiting for work")
}
}
}
func main() {
ch := make(chan bool)
for i := 1; i <= 5; i++ {
go worker(i, ch)
}
for i := 0; i < 2; i++ {
ch <- true
}
}
Comment prévenir
- Utiliser des canaux avec une taille définie pour éviter les écrabouillages.
- Assurer que chaque case
casedans unselectest couverte par une autre case ou undefault. - Éviter l'utilisation excessive de canaux bloquants dans la même fonction.
Erreur N2 : Race Condition
Le problème
Une race condition se produit lorsque deux goroutines accèdent simultanément à une variable partagée et au moins l'une d'entre elles effectue un changement. Cela peut entraîner des résultats imprévisibles ou indésirables.
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 3; i++ {
fmt.Println("Worker", id, "is working")
time.Sleep(time.Millisecond)
}
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go worker(1, &wg)
go worker(2, &wg)
wg.Wait()
}
Pourquoi c'est une erreur
Une race condition peut entraîner des bugs difficiles à repérer et à corriger. Elle peut également causer des comportements imprévisibles ou non déterministes.
La solution
Pour éviter une race condition, vous devez utiliser les mécanismes de synchronisation fournis par Go, comme sync.Mutex, sync.RWMutex, ou sync.Cond.
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup, mu *sync.Mutex) {
defer wg.Done()
for i := 0; i < 3; i++ {
mu.Lock()
fmt.Println("Worker", id, "is working")
time.Sleep(time.Millisecond)
mu.Unlock()
}
}
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
wg.Add(2)
go worker(1, &wg, &mu)
go worker(2, &wg, &mu)
wg.Wait()
}
Comment prévenir
- Utiliser des mécanismes de synchronisation comme
sync.Mutexousync.RWMutex. - Éviter l'utilisation de variables partagées entre goroutines autant que possible.
- Faire attention aux boucles et structures conditionnelles qui pourraient entraîner des conditions critiques.
Erreur N3 : Nil Pointer Exception
Le problème
Une nil pointer exception se produit lorsque vous essayez d'accéder à une méthode ou une propriété d'un pointeur nul. Cela peut entraîner un programme qui s'arrête subitement et générer des erreurs difficiles à déboguer.
package main
import "fmt"
type Person struct {
Name string
}
func (p *Person) SayHello() {
fmt.Println("Hello, my name is", p.Name)
}
func main() {
var person *Person = nil
person.SayHello()
}
Pourquoi c'est une erreur
Une nil pointer exception peut entraîner des comportements imprévisibles ou non déterministes. Elle peut également rendre l'application moins robuste et plus susceptible de plantage.
La solution
Pour éviter une nil pointer exception, vous devez vérifier si un pointeur est nul avant d'y accéder. Vous pouvez utiliser une instruction if pour effectuer cette vérification.
package main
import "fmt"
type Person struct {
Name string
}
func (p *Person) SayHello() {
if p != nil {
fmt.Println("Hello, my name is", p.Name)
} else {
fmt.Println("Person is nil")
}
}
func main() {
var person *Person = nil
person.SayHello()
}
Comment prévenir
- Vérifier toujours si un pointeur est nul avant d'y accéder.
- Utiliser des fonctions d'aide pour éviter les erreurs liées aux pointeurs nuls.
- Faire attention aux initialisations de variables et à la gestion des ressources.
Erreur N4 : Memory Leak
Le problème
Un memory leak se produit lorsque vous allouez de la mémoire qui ne peut pas être libérée. Cela peut entraîner une perte progressive de la mémoire disponible pour l'application, ce qui peut finir par entraîner un plantage.
package main
import (
"time"
)
func leak() {
for i := 0; i < 1000000; i++ {
go func() {
data := make([]byte, 1024)
time.Sleep(time.Second) // Do something with data
}()
}
}
func main() {
leak()
}
Pourquoi c'est une erreur
Un memory leak peut entraîner une perte progressive de la mémoire disponible pour l'application. Cela peut finir par entraîner un plantage et rendre l'application moins robuste.
La solution
Pour éviter un memory leak, vous devez vous assurer que toutes les allocations de mémoire sont correctement libérées. Vous pouvez utiliser des outils comme pprof pour détecter les fuites de mémoire.
package main
import (
"time"
)
func leak() {
for i := 0; i < 1000000; i++ {
go func() {
data := make([]byte, 1024)
time.Sleep(time.Second) // Do something with data
data = nil // Free memory
}()
}
}
func main() {
leak()
}
Comment prévenir
- Libérer explicitement la mémoire allouée avec
defer. - Utiliser des outils comme
pprofpour détecter les fuites de mémoire. - Éviter l'utilisation excessive de structures et de canaux sans libération.
Erreur N5 : Invalid Argument
Le problème
Une invalid argument se produit lorsque vous passez un argument invalide à une fonction. Cela peut entraîner des résultats imprévisibles ou indésirables, ainsi que des erreurs difficiles à repérer et à corriger.
package main
import (
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(result)
}
}
Pourquoi c'est une erreur
Une invalid argument peut entraîner des comportements imprévisibles ou non déterministes. Elle peut également rendre l'application moins robuste et plus susceptible de plantage.
La solution
Pour éviter une invalid argument, vous devez vérifier les arguments passés à une fonction avant d'y utiliser. Vous pouvez utiliser des instructions if pour effectuer cette vérification.
package main
import (
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(result)
}
}
Comment prévenir
- Vérifier toujours les arguments passés à une fonction.
- Utiliser des fonctions d'aide pour éviter les erreurs liées aux arguments invalides.
- Faire attention aux conditions spéciales et aux valeurs edge.
Erreur N6 : Timeout
Le problème
Un timeout se produit lorsque une opération prend trop de temps. Cela peut entraîner un programme qui s'arrête subitement et générer des erreurs difficiles à déboguer.
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Worker stopped", ctx.Err())
case <-time.After(5 * time.Second):
fmt.Println("Work done")
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
worker(ctx)
}
Pourquoi c'est une erreur
Un timeout peut entraîner des comportements imprévisibles ou non déterministes. Elle peut également rendre l'application moins robuste et plus susceptible de plantage.
La solution
Pour éviter un timeout, vous devez utiliser les mécanismes de temps limité fournis par Go, comme context.WithTimeout ou time.After. Vous pouvez également utiliser des instructions select pour gérer les cas où une opération prend trop de temps.
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Worker stopped", ctx.Err())
case <-time.After(5 * time.Second):
fmt.Println("Work done")
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
worker(ctx)
}
Comment prévenir
- Utiliser des mécanismes de temps limité comme
context.WithTimeoutoutime.After. - Faire attention aux opérations qui pourraient prendre beaucoup de temps.
- Éviter l'utilisation excessive de boucles et structures conditionnelles qui pourraient entraîner des conditions critiques.
Erreur N7 : Concurrency Bugs
Le problème
Une concurrency bug se produit lorsque deux ou plusieurs goroutines s'attendent mutuellement sur des conditions qui ne peuvent jamais être remplies. Cela peut entraîner une application qui se bloque indéfiniment.
package main
import (
"fmt"
"time"
)
func worker(id int, ch chan bool) {
for {
select {
case <-ch:
fmt.Println("Worker", id, "is working")
time.Sleep(time.Second)
ch <- true // Deadlock here
}
}
}
func main() {
ch := make(chan bool)
for i := 1; i <= 5; i++ {
go worker(i, ch)
}
<-ch // This line is never reached, causing a deadlock
}
Pourquoi c'est une erreur
Une concurrency bug peut entraîner une perte de performance significative car il empêche les autres goroutines d'avancer. En outre, cela rend l'application imprévisible et difficile à déboguer.
La solution
Pour éviter un concurrency bug, vous devez vous assurer que chaque case case dans un select est couverte par une autre case ou un default. Dans ce cas, nous devons ajouter une case default pour permettre au programme de continuer même sans reçu.
package main
import (
"fmt"
"time"
)
func worker(id int, ch chan bool) {
for {
select {
case <-ch:
fmt.Println("Worker", id, "is working")
time.Sleep(time.Second)
ch <- true // Fixed deadlock here
default:
fmt.Println("Worker", id, "waiting for work")
}
}
}
func main() {
ch := make(chan bool)
for i := 1; i <= 5; i++ {
go worker(i, ch)
}
for i := 0; i < 2; i++ {
ch <- true
}
}
Comment prévenir
- Utiliser des canaux avec une taille définie pour éviter les écrabouillages.
- Assurer que chaque case
casedans unselectest couverte par une autre case ou undefault. - Éviter l'utilisation excessive de canaux bloquants dans la même fonction.
Erreur N8 : Resource Leak
Le problème
Un resource leak se produit lorsque vous allouez des ressources qui ne peuvent pas être libérées. Cela peut entraîner une perte progressive des ressources disponibles pour l'application, ce qui peut finir par entraîner un plantage.
package main
import (
"time"
)
func leak() {
for i := 0; i < 1000000; i++ {
go func() {
file, err := os.Open("file.txt")
if err != nil {
fmt.Println(err)
}
defer file.Close()
time.Sleep(time.Second) // Do something with file
}()
}
}
func main() {
leak()
}
Pourquoi c'est une erreur
Un resource leak peut entraîner une perte progressive des ressources disponibles pour l'application. Cela peut finir par entraîner un plantage et rendre l'application moins robuste.
La solution
Pour éviter un resource leak, vous devez vous assurer que toutes les allocations de ressources sont correctement libérées. Vous pouvez utiliser des instructions defer pour effectuer cette libération.
package main
import (
"os"
"time"
)
func leak() {
for i := 0; i < 1000000; i++ {
go func() {
file, err := os.Open("file.txt")
if err != nil {
fmt.Println(err)
}
defer file.Close()
time.Sleep(time.Second) // Do something with file
}()
}
}
func main() {
leak()
}
Comment prévenir
- Libérer explicitement la ressource allouée avec
defer. - Utiliser des outils comme
pprofpour détecter les fuites de ressources. - Éviter l'utilisation excessive de structures et de canaux sans libération.