Reputation: 167
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?
Upvotes: 1
Views: 2634
Reputation: 42413
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.
Upvotes: -1
Reputation: 51512
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
.
Upvotes: 6