On Tue, Jul 19, 2022 at 08:28:02AM -0700, 'andreas graeper' via golang-nuts 
wrote:

> for i=0;i<10;i++ { go func () { /* const c=i OR var v=i */
>  fmt.Println("f:beg i=",i) // here c or v instead 
> // action 
>  fmt.Println("f:end i=",i) // here c or v instead 
> }}
> when this routines get interrupted then beg-i and end-i differ
> now i want at the beginning of the routine a const copy `const c int = i`
> but its not allowed and `var v int = i` does not work, too.
> 
> does an instance of a go-routine has no own stack-frame like a normal 
> function (c i.e) and therein local/private variables ? 

This?

https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables

In short:

 - Your goroutines run so-called anonymous functions - those defined "inline"
   and having no name, some folks call them "lambdas".

   Anonymous functions works like "closures": they "close over" any variable
   accessed in such a function but declared in any of the outer scopes
   accessible to the function.

 - A closure captures its external variables via reference - that is, if you
   create multiple closures in the same lexical scope, they all will reference
   the same variables. To say that in other words, they do not receive copies
   of such variables; they reference the original variables.

 - In your code snippet you start 10 goroutines passing each a new closure,
   each referencing the same loop variable, i.

Hence, in your example you create the condition for a data race of 10
goroutines over the same variable i. The behaviour of this code is hence
undefined. If you were to `go build -race` it (or `go run -race`, FWIW),
the runtime would crash your running program.


Conceptually two solutions are possible:

 - Make your anonymous functions not close over that shared variable.

   This can be done by defining one or more arguments in the function's
   definition and pass the values the function has to operate on via that
   parameters when calling it. Since passing of parameters is done via
   copying them, each anonymous function will have their own copies.

   In your simple case you could do what PeterGo proposed - something like:

   go func (int i) { ... }(i)

   If the set of variables to operate is huge so as being impractical to
   be passed via arguments, you usually either define a helper `struct`
   type containing all the required stuff and pass it as a single parameter
   or stop using anonymous functions altogether and define a custom type
   with a method, and to run a gorotine, you construct an instance of this
   type and then call the goroutine to run that method on that instance.

   (To recap: do not be attached to passing anonymous functions to goroutines:
   it indeed is a cool feature, often useful, but it is so widely demonstrated
   by various guides and blog posts that some novice Go programmers do not
   even understand that you can pass anything "callable" to the `go`
   statement, not only an anonymous function. Yes, I've witnessed people with
   this mind wart in interviews at my $dayjob.)

 - Create a copy of the loop iteration variable and capture _it_ in the
   anonymous function - like this:

   for i := 0, i < 10; i++ {
     i := i
     go func() { fmt.Println(i) }()
   }

   This "trick" works because in Go, all loop statements have two scopes: the
   outer scope is where the loop iteration variables live, and the inner one
   is the loop's body; hence in the presented example we create a variable
   named "i" in the inner scope and initialize it with the value currently
   held in the variable named "i" from the outer scope. The "inner" "i" is
   different on each iteration - or, to say that in other words, - is
   recreated anew on each iteration, so it's safe to capture it in a goroutine
   started on that iteration - provided, of course, that it's the sole
   goroutine capturing that variable and started on that iteration.

What approach to pick depends on the use case, personal aesthetics and
whatever the person(s) doing code review on your team prefer.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/20220720100059.44upqj4vcqpnuf7d%40carbon.

Reply via email to