Why is my for loop not looping the elements right?

Issue

Starting from gobyexample for variadic functions and WaitGroup, I came up with this to run a number of functions as goroutines in parallel and synchronize (prints are debug only):

package main

import (
    "fmt"
    "sync"
    "time"
)

func WaitForFuncs(funcs ...func()) {
  fmt.Print("funcs are: ", funcs, "\n")
  var wg sync.WaitGroup
  for _, f := range funcs {
    fmt.Print("f is: ", f, "\n")
    wg.Add(1)
    go func() {
      defer wg.Done()
      f()
    }()
  }
  wg.Wait()
}


func main() {
    num := 4
    WaitForFuncs(
      func() {
        fmt.Printf("Worker foo starting, num=%d\n", num)
        time.Sleep(time.Second)
        fmt.Printf("Worker foo done\n")
      },
      func() {
        fmt.Printf("Worker bar starting\n")
        time.Sleep(time.Second)
        fmt.Printf("Worker bar done\n")
      },
      func() {
        fmt.Printf("Worker baz starting\n")
        time.Sleep(time.Second)
        fmt.Printf("Worker baz done\n")
      },
    )
}

But the output surprises me:

funcs are: [0x4a0360 0x4a0470 0x4a0540]
f is: 0x4a0360
f is: 0x4a0470
f is: 0x4a0540
Worker bar starting
Worker baz starting
Worker baz starting
Worker bar done
Worker baz done
Worker baz done

Why does it print bar baz baz and not foo bar baz?

Solution

You have to use an intermediate variable in the loop, otherwise the value gets overwritten before executed. That is the reason why you only see the last func three times.

func waitForFuncs(funcs ...func()) {
    var wg sync.WaitGroup
    for _, f := range funcs {
        intermediate := f
        wg.Add(1)
        go func(inner func()) {
            defer wg.Done()
            inner()
        }(intermediate)
    }
    wg.Wait()
}

Answered By – tobi.g

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.