Πώς να ολοκληρώσετε σωστά την εκτέλεση πολλαπλών goroutines στο Go;
sobes.tech AI
Απάντηση από AI
Υπάρχουν μερικοί συνηθισμένοι τρόποι:
-
Χρήση του context: Η προτεινόμενη προσέγγιση για ακύρωση λειτουργιών σε δομή δέντρου κλήσεων. Το context μπορεί να μεταφέρει σήμα ακύρωσης μέσω καναλιού.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Γουρουίντα %d ολοκληρώνεται: %v\n", id, ctx.Err()) return default: fmt.Printf("Γουρουίντα %d λειτουργεί...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go worker(ctx, 1) go worker(ctx, 2) time.Sleep(3 * time.Second) cancel() // Αποστολή σήματος ακύρωσης time.Sleep(1 * time.Second) // Δίνουμε χρόνο στις γουρουίντες να ολοκληρωθούν fmt.Println("Το κύριο πρόγραμμα ολοκληρώθηκε.") } -
Χρήση καναλιών: Αποστέλλοντας σήμα (π.χ., κενή δομή
struct{}) μέσω ειδικού καναλιού, μπορούμε να ενημερώσουμε τη γουρουίντα για την ανάγκη τερματισμού.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Γουρουίντα %d ολοκληρώνεται μέσω καναλιού\n", id) return default: fmt.Printf("Γουρουίντα %d λειτουργεί...\n", id) time.Sleep(1 * time.Second) } } } func main() { stopChan := make(chan struct{}) go worker(stopChan, 1) go worker(stopChan, 2) time.Sleep(3 * time.Second) close(stopChan) // Το κλείσιμο του καναλιού σηματοδοτεί το τερματισμό time.Sleep(1 * time.Second) // Δίνουμε χρόνο στις γουρουίντες να ολοκληρωθούν fmt.Println("Το κύριο πρόγραμμα ολοκληρώθηκε.") } -
Χρήση
sync.WaitGroup(για αναμονή, όχι για σήμα): ΤοWaitGroupχρησιμοποιείται για την αναμονή της ολοκλήρωσης μιας ομάδας γουρουίντων, αλλά από μόνο του δεν παρέχει μηχανισμό διακοπής. Συχνά χρησιμοποιείται σε συνδυασμό με context ή κανάλια.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Μειώνει τον μετρητή του WaitGroup όταν ολοκληρώνει η γουρουίντα for { select { case <-ctx.Done(): fmt.Printf("Γουρουίντα %d ολοκληρώνεται\n", id) return default: fmt.Printf("Γουρουίντα %d λειτουργεί...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Αρχικοποιούμε το WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Σήμα ακύρωσης wg.Wait() // Περιμένουμε να ολοκληρωθούν όλες οι γουρουίντες fmt.Println("Το κύριο πρόγραμμα ολοκληρώθηκε.") }
Η επιλογή εξαρτάται από την συγκεκριμένη περίπτωση χρήσης και την πολυπλοκότητα των αλληλεπιδράσεων των γουρουίντων. Το context προτιμάται γενικά για διαδοχικές ακυρώσεις.