Ontwerp een efficiënte Go-concurrentiepattern
description
Met deze prompt kan ik leren hoe ik concurrerende processen in Go efficiënt beheer, inclusief foutafhandeling en synchronisatie, wat mijn Go-programma's robuuster en performanter maakt. Het biedt een praktische aanpak die verder gaat dan basisvoorbeelden en helpt bij het oplossen van complexe concurrency-problemen.
prompt
Help mij met het ontwerpen van een Go-programma dat concurrerende taken efficiënt beheert met behulp van goroutines en kanalen. Beschrijf ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Hier is een basisvoorbeeld van een Go-programma dat drie taken coördineert en synchroniseert met goroutines en kanalen, inclusief foutafhandeling:
```go
package main
import (
"fmt"
"sync"
)
// taakfunctie die een fout kan retourneren
func taak(id int, wg *sync.WaitGroup, foutCh chan<- error) {
defer wg.Done()
// Simuleer werk
fmt.Printf("Taak %d gestart.\n", id)
// Bijvoorbeeld: een fout conditioneel laten optreden
if id == 2 {
foutCh <- fmt.Errorf("Fout in taak %d", id)
return
}
// Werk voltooid
fmt.Printf("Taak %d voltooid.\n", id)
foutCh <- nil
}
func main() {
var wg sync.WaitGroup
foutCh := make(chan error, 3) // buffer om alle resultaten op te vangen
// Start drie taken
for i := 1; i <= 3; i++ {
wg.Add(1)
go taak(i, &wg, foutCh)
}
// Wacht tot alle taken klaar zijn
go func() {
wg.Wait()
close(foutCh)
}()
// Verwerk resultaten
for err := range foutCh {
if err != nil {
fmt.Println("Fout gevonden:", err)
}
}
fmt.Println("Alle taken voltooid.")
}
```
**Uitleg:**
- We gebruiken een `sync.WaitGroup` om te wachten tot alle goroutines klaar zijn.
- Elke taak voert een stukje werk uit en stuurt een foutmelding (of nil) terug via een kanaal `foutCh`.
- Nadat alle taken gestart zijn, wacht de hoofdfunctie op hun voltooiing en sluit dan het kanaal af.
- Vervolgens worden alle foutmeldingen verwerkt. Als één van de taken een fout geeft, wordt dat weergegeven.
**Kort samengevat:**
- Goroutines zorgen voor gelijktijdige uitvoering.
- Kanalen coördineren en verzamelen resultaten.
- `sync.WaitGroup` zorgt voor synchronisatie.
- Foutafhandeling gebeurt via het kanaal, waardoor je effectief fouten kunt detecteren en afhandelen in een concurrerende omgeving.
provider: deepseek
model: deepseek-chat
Zeker! Ik zal je helpen bij het ontwerpen van een Go-programma met goroutines en kanalen. Hier is een complete uitleg met voorbeeldcode:
## Basisstructuur met 3 gecoördineerde taken
```go
package main
import (
"context"
"errors"
"fmt"
"sync"
"time"
)
// Taak 1: Data verwerken
func verwerkData(ctx context.Context, data []int, resultChan chan<- int, errChan chan<- error) {
defer close(resultChan)
for _, num := range data {
select {
case <-ctx.Done():
errChan <- errors.New("verwerkData: context geannuleerd")
return
default:
if num == 0 {
errChan <- errors.New("verwerkData: nul waarde gedetecteerd")
continue
}
result := num * 2
resultChan <- result
time.Sleep(100 * time.Millisecond) // Simuleer werk
}
}
}
// Taak 2: Resultaten verzamelen
func verzamelResultaten(ctx context.Context, resultChan <-chan int, finalChan chan<- []int, errChan chan<- error) {
var results []int
for {
select {
case <-ctx.Done():
errChan <- errors.New("verzamelResultaten: context geannuleerd")
return
case result, ok := <-resultChan:
if !ok {
finalChan <- results
return
}
results = append(results, result)
}
}
}
// Taak 3: Monitoring en logging
func monitorTaken(ctx context.Context, wg *sync.WaitGroup, errChan <-chan error) {
defer wg.Done()
for {
select {
case <-ctx.Done():
return
case err, ok := <-errChan:
if !ok {
return
}
fmt.Printf("Fout gedetecteerd: %v\n", err)
// Hier kun je extra fouthandeling toevoegen
}
}
}
func main() {
// Maak context met timeout voor graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// Kanalen voor communicatie
resultChan := make(chan int, 10)
finalChan := make(chan []int, 1)
errChan := make(chan error, 5)
var wg sync.WaitGroup
// Start monitoring taak
wg.Add(1)
go monitorTaken(ctx, &wg, errChan)
// Start dataverwerking
inputData := []int{1, 2, 3, 0, 5} // Inclusief foutconditie (0)
go verwerkData(ctx, inputData, resultChan, errChan)
// Start resultaatverzameling
go verzamelResultaten(ctx, resultChan, finalChan, errChan)
// Wacht op resultaten
select {
case <-ctx.Done():
fmt.Println("Timeout: niet alle taken voltooid")
case finalResults := <-finalChan:
fmt.Printf("Voltooide resultaten: %v\n", finalResults)
}
// Graceful shutdown
cancel()
close(errChan)
wg.Wait()
fmt.Println("Programma afgesloten")
}
```
## Belangrijke concepten uitgelegd:
### 1. **Context Usage**
- `context.WithTimeout()` zorgt voor timeouts
- `ctx.Done()` detecteert annuleringen
- Graceful shutdown mogelijkheid
### 2. **Kanaal Management**
- Gebufferde kanalen voorkomen deadlocks
- `close()` om kanalen netjes af te sluiten
- Select statements voor non-blocking operations
### 3. **Foutafhandeling**
- Dedicated error kanaal (`errChan`)
- Fouten worden asynchroon verwerkt
- Errors stoppen niet het hele programma
### 4. **Synchronisatie**
- `sync.WaitGroup` voor taakcoördinatie
- Kanalen voor data-uitwisseling
- Context voor lifecycle management
## Uitbreidingen en best practices:
```go
// Advanced error handling met custom error types
type TaskError struct {
Task string
Message string
Time time.Time
}
func (e *TaskError) Error() string {
return fmt.Sprintf("%s: %s (op %v)", e.Task, e.Message, e.Time)
}
// Rate limiting met ticker
func rateLimitedTask(ctx context.Context, rate time.Duration) {
ticker := time.NewTicker(rate)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// Uitvoeren met rate limiting
}
}
}
```
## Tips voor productiecode:
1. Gebruik `context.Context` voor cancellation
2. Implementeer circuit breakers voor fouttolerantie
3. Gebruik `sync.Pool` voor object recycling
4. Monitor goroutine leaks met runtime metrics
5. Gebruik `select` met default voor non-blocking operations
Dit patroon biedt een robuuste basis voor concurrente taken met propere foutafhandeling en synchronisatie!