GoRoutines not in main end up in deadlock

  Kiến thức lập trình

I’ve been writing Go for a while, but just recently needed to actually utilize goroutines/asynchronous actions. I’m having a hard time understanding how things are supposed to work.

  • I have a main function, which is just the entry point to my cli tool (so it just runs cmd.ExecuteContext() and checks for an error).
  • I have a command, project doSomething, that needs to run some go routines in a for loop.
  • I am not exactly sure what the issue is, I’ve hit: fatal error: all goroutines are asleep - deadlock! but when I think I’ve solved that my command just seems to hang forever (I think this happens when one of the go routines hits an error…?).

Can someone help me understand the pattern I am supposed to implement here?

main.go

func main() {
    ctx := context.Background()
    if err := cmd.NewRootCommand().ExecuteContext(ctx); err != nil {
        log.Err(err)
        os.Exit(1)
    }
}

anotherfile.go

func NewRootCommand() *cobra.Command {
    rootCmd.AddCommand(newProjectCmd())
    return rootCmd
}

func newProjectCmd() *cobra.Command {
    projCmd := &cobra.Command{
        Use:   "project",
        Short: "Interact with groups",
    }
    projCmd.AddCommand(newDoSomethingCmd())
    return projCmd
}

func newDoSomethingCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use: "doSomething",
        Run: func(cmd *cobra.Command, args []string) {
            
            // stuff happens here, but no go routines or anything.

            err := project.DoSomething(cmd.Context())
            if err != nil {
                os.Exit(1)
            }
        },
    }
    return cmd
}

lastfile.go (the project package)

func DoSomething(ctx context.Context) error {
        // lots of stuff happens
        err := firstFunction(ctx) 
        if err != nil {
            return err
        }
        return nil
}

func firstFunction(ctx context.Context) error {
        // stuff here no go routines
        err := functionWithGoRoutines(ctx) 
        if err != nil {
            return err
        }

        // gets called multiple times with different parameters -- at this point I want the above to FINISH though before moving on to here
        err := functionWithGoRoutines(ctx) 
        if err != nil {
            return err
        }
        return nil
}

func functionWithGoRoutines(ctx context.Context) error {
        // lots of stuff happens, nothing special

        var WG *sync.WaitGroup
        for _, item := range items {
             wg.Add(1)
             i := item
             go func() {
        defer wg.Done()
                // do stuff
                // this is the stuff i want to happen in parallel
                // includes functions that can return an error. but i want the other runs to finish because ending. ie, if item[0] returns an error, i want the go routine for item[1] to have time to finish before this whole function ends
             }()
        }

    wg.Wait()
        var err error
    if len(errors) > 0 {
        err = fmt.Errorf("multiple errors occurred with doing the things: %v", errors)
    }
    return err
}

Please let me know if this is already answered somewhere else and I missed it! From what I could tell, nothing was describing my situation exactly. Also please let me know if anything is unclear about what I am trying to do!

My end goal is to have a function which loops through a slice, and starts a go routine for each item in the slice. I want each item’s go routine to have time to finish before exiting the outer function. I want to collect the errors (if any) and return them once all the go routines have finished.

I also tried creating the wait group in main.go, and passing it all the way through to my function. I saw somewhere i should declare it globally… but wasn’t sure where?

New contributor

Kia Martinez is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

LEAVE A COMMENT