1

Fairly new to golang. I am a bit confused about go's variable scope. i have the following toy program

package main

import "sync"
import "time"
import "fmt"
import "math/rand"

func main() {
    go main_helper()
    time.Sleep(time.Duration(1000000) * time.Millisecond)

}
func main_helper() {
    rand.Seed(time.Now().UnixNano())

    count := 0
    finished := 0
    var mu sync.Mutex
    cond := sync.NewCond(&mu)

    for i := 0; i < 10; i++ {
        go func(i int) {
            vote := requestVote(i)
            mu.Lock()
            defer mu.Unlock()
            if vote {
                count++
            }
            fmt.Printf("cur_count: %d\n", count)
            finished++
            cond.Broadcast()
        }(i)
    }

    mu.Lock()
    for count < 5 {
        cond.Wait()
    }
    if count >= 5 {
        println("received 5+ votes!")
    } else {
        println("lost")
    }
    mu.Unlock()
    fmt.Printf("Exited main loop\n")

}

func requestVote(i int) bool {
    if i > 6 {
        time.Sleep(time.Duration(1000) * time.Millisecond)
    } else {
        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
    }
    fmt.Printf("go routine: %d requested vote\n", i)
    return true
}

When Running this in the Go playground i got the following output:

go routine: 0 requested vote
cur_count: 1
go routine: 6 requested vote
cur_count: 2
go routine: 1 requested vote
cur_count: 3
go routine: 4 requested vote
cur_count: 4
go routine: 2 requested vote
cur_count: 5
received 5+ votes!
Exited main loop
go routine: 3 requested vote
cur_count: 6
go routine: 5 requested vote
cur_count: 7
go routine: 7 requested vote
cur_count: 8
go routine: 8 requested vote
cur_count: 9
go routine: 9 requested vote
cur_count: 10

Which raise a question in that when main_helper() exit, why doesn't the local variable like count and mu go out of scope? Why are we still seeomg unfinished go routine updating the count variable correctly?

1
  • Burak Serdar's answer shows you how the Go compiler figures out which variables need special treatment (the escape analysis part), but more generally, this is referred to in computing as a closure. Many languages have closures. Commented Aug 24, 2020 at 2:04

2 Answers 2

6

It is the result of "escape analysis". The compiler realizes that the variable count escapes the function main_helper because it is used in the goroutine, so that variable is allocated on heap instead of on stack. In general, you can return pointers to local variables, and because of escape analysis, compiler allocates that variable on the heap, like:

type X struct {
   ...
}

func NewX() *X {
  return &X{}
}

This is a common constructor-like pattern to initialize structs. This code is equivalent to:

func NewX() *X {
  return new(X)
}

In your program, the count:=0 declaration is equivalent to:

count:=new(int)
*count=0

and the goroutine keeps a pointer to count. Same for finished.

Sign up to request clarification or add additional context in comments.

6 Comments

Ah i see, than you so much!
Is there any benefit of passing the variable via function parameter instead of just accessing the local variable? go func(i int){ /* code */ }(localVar) instead of just using localVar inside the func?
@apollo, Passing a variable as argument passes a copy of it and avoids it from escaping to heap
@BurakSerdar thank you. But I thought the Go compiler will keep local variables outside of the go routine on the heap and the GC will clean up when there are no references left. So I am not sure what the advantage of a copy would be.
@Apollo, yes. But when a variable escapes to heap like that it becomes a shared variable between the main goroutine and the new goroutine. You have to be careful not to creare a memory race.
|
-1

How does go routine access local variable from calling function after function exited?

Any compiler conforming to the Go Language Spec may use whatever technique its author choose to implement, e.g. putting all variables on the heap, not releasing memory at all, use reference counting or put just some variables on the stack.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.