Why does this goroutin loop just repeat the last value?

Asked

Viewed 130 times

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 simple, using the unique.print() will show on the screen the value of id, but it doesn’t work properly. You’d be expected to return 1, 3 and 5, but he returns:

5
5
5

Look at this here.

I can not understand why this does not work, because using in a "normal" way, without the use of goroutines, directly using valor.print() it works.

It seems to me that this is something related to the use of goroutine, because this occurs?


Using:

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

Seems to fix the problem, but I don’t know why.

1 answer

1


I already answered this in C#.

A Goroutine is a call from a code controlled by Runtime for asynchronous parallel execution or not. Obviously the code presented there is executed lazily (Lazy), that is, he will be called elsewhere and not there where you see him written. This is done with some delegation mechanism, probably with an enclosure.

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

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

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)
}

Behold working in the ideone. And in the Playground Go. Also put on the Github for future reference.

To me that’s design wrong language. It should highlight the variable on its own. C# was like that and fixed it, but the designers of Go are very stubborn, one of the reasons language does not take off once.

  • Javascript has the same behavior, just by capturing the variable. As long as the rule is clear (and is), I don’t see anything special about it.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.