Home > OS >  Why do goroutines not execute until the for-loop has terminated?
Why do goroutines not execute until the for-loop has terminated?

Time:10-13

Like most of you, I'm familiar with the fact that Go reuses the iterator variable in a for-loop, so closures for each goroutine will capture the same variable. A typical example of this the following code which will always print the final value of the loop from each goroutine:

func main() {
    for i := 0; i < 5; i   {
        go func() {
            fmt.Println(i) // prints 5 each time
        }()
    }
    time.Sleep(100 * time.Millisecond)
}

What I haven't been able to find an explanation of, is why the goroutines do not execute until after the loop is completed. Even the following code produces the value of ii as 10 which is set after the goroutine is called:

func main() {
    for i := 0; i < 5; i   {
        ii := i
        go func() {
            fmt.Println(ii) // prints 10 each time...!
        }()
        ii = 10
    }
    time.Sleep(100 * time.Millisecond)
}

The closest thing to an explanation I've read is that for-loops typically execute faster than goroutines. This raises two questions for me: 1. Is that true? 2. Why?

CodePudding user response:

Anything is possible because the program has a data race.

Setting aside the data race, there's no evidence that the goroutines execute after the for loop is completed. The value 10 is assigned to ii in the statement after the goroutine is started, not after the for loop.

The memory model allows for the compiler to optimize the two ii assignments to a single assignment at the start of the for loop body. It could be that the goroutines execute immediately, but the goroutines see the value of 10 from the optimization.

CodePudding user response:

Never assume the order of execution when dealing with more than one goroutine - no matter how tempting it may be. Don't put in sleeps; don't call runtime.Gosched; don't assume anything.

The only guaranteed way to ensure order of execution is via synchronization methods such as channels, sync.Mutex, sync.WaitGroups etc.

CodePudding user response:

you should use the goroutine like this:

package main

import "fmt"
import "time"

func main() {
    for i := 0; i < 5; i   {
        ii := i
        go func(k int) {
            fmt.Println(k) // prints 10 each time...!
        }(ii)
        ii = 10
    }
    time.Sleep(100 * time.Millisecond)
}
  • Related