Why does this loop with goroutines only repeat the last value?

1

This loop is always repeating the last value, consider the following:

type unique struct {
    id, nonce uint64
}

func (unique *unique) print() {

    fmt.Println(unique.id)

}

func main() {

    teste := []unique{unique{1, 2}, unique{3, 4}, unique{5, 6}}

    for _, valor := range teste {

        go valor.print()

    }

    time.Sleep(4 * time.Second)

}

It's pretty simplistic, using unique.print() will display the id on the screen, however this does not work correctly. It would be expected to return 1 , 3 and 5 , but it returns:

5
5
5

Check this here .

I can not understand why this does not work, because using "normal" without using goroutines , directly using valor.print() it works .

I think this is something related to the use of goroutine , why does this occur?

Using:

teste := []*unique{&unique{1,2}, &unique{3,4}, &unique{5, 6}}

It seems to fix the problem, but I do not know why.

    
asked by anonymous 24.06.2017 / 11:19

1 answer

1

I already answered this in C # .

A Goroutine is a call of a runtime-controlled code for parallel asynchronous execution or not. Obviously the code presented there runs lazily ( lazy ), meaning it will be called somewhere else and not where you see it written. This is done with some delegation mechanism, probably with a cloister .

Then the variable that is enclosed has a reference to the original variable. When it is going to execute the value to be used is the value that is at the end of the loop, then valor at the end is unique{5, 6} , the rest are ignored. When using the reference it solves this.

Another way would be to do this without changing the data semantics:

package main

import (
    "fmt"
    "time"
)

type unique struct {
    id, nonce uint64
}

func (unique *unique) print() {
    fmt.Println(unique.id)
}

func main() {
    teste := []unique{unique{1, 2}, unique{3, 4}, unique{5, 6}}
    for _, valor := range teste {
        valorTmp := valor;
        go valorTmp.print()
    }
    time.Sleep(4 * time.Second)
}

See running on ideone . And on Playground Go . Also put it in GitHub for future reference .

For me this is wrong design of language. She should highlight the variable on her own. C # was like this and fixed it, but the designers of GO are very stubborn, one of the reasons why the language does not take off once.

    
24.06.2017 / 19:15