How to Gracefully Terminate Goroutines and Handle Errors in Go?

Mary-Kate Olsen
Release: 2024-11-12 07:21:02
Original
890 people have browsed it

How to Gracefully Terminate Goroutines and Handle Errors in Go?

Idiomatic Goroutine Termination and Error Handling: A Case Study

In Go, handling goroutine termination is often a challenge for developers. One common issue is ensuring proper cleanup of goroutines when an error occurs. This article demonstrates an elegant and idiomatic solution to this problem using error grouping.

Consider the following example:

package main

import (
        "sync"
        "time"
)

func fetchAll() error {
        wg := sync.WaitGroup{}
        errs := make(chan error)

        // run all the http requests in parallel
        for i := 0; i < 4; i++ {
                wg.Add(1)
                go func(i int) {
                        defer wg.Done()

                        // pretend this does an http request and returns an error
                        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
                        errs <- fmt.Errorf("goroutine %d's error returned", i)
                }(i)
        }

        // wait until all the fetches are done and close the error
        // channel so the loop below terminates
        go func() {
                wg.Wait()
                close(errs)
        }()

        // return the first error
        for err := range errs {
                if err != nil {
                        return err
                }
        }

        return nil
}
Copy after login

This implementation has a critical flaw: it leaks goroutines. The fix is to use an error group:

package main

import (
        "context"
        "fmt"
        "math/rand"
        "time"

        "golang.org/x/sync/errgroup"
)

func fetchAll(ctx context.Context) error {
        errs, ctx := errgroup.WithContext(ctx)

        // run all the http requests in parallel
        for i := 0; i < 4; i++ {
                errs.Go(func() error {
                        // pretend this does an http request and returns an error                                                  
                        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)                                               
                        return fmt.Errorf("error in go routine, bailing")                                                      
                })
        }

        // Wait for completion and return the first error (if any)                                                                 
        return errs.Wait()
}
Copy after login

An error group automatically waits for all goroutines to complete successfully or cancels those remaining in case of an error. In the above case, the first goroutine that encounters an error triggers cancellation of all others, and the error is propagated back to the caller. The use of context ensures graceful termination when the surrounding context is canceled.

The above is the detailed content of How to Gracefully Terminate Goroutines and Handle Errors in Go?. For more information, please follow other related articles on the PHP Chinese website!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template