函数退出后,goroutine如何从调用函数访问局部变量?

对golang相当陌生。我对 go 的变量范围有点困惑。我有以下玩具程序


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

}

在 Go 操场上运行此程序时,我得到以下输出:


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

这引发了一个问题,即main_helper()退出时,为什么局部变量不喜欢count并mu超出范围?为什么我们仍然看到未完成的 goroutine 正确更新计数变量?


人到中年有点甜
浏览 102回答 2
2回答

慕田峪4524236

这是“逃逸分析”的结果。编译器意识到该变量count转义了函数main_helper,因为它在 goroutine 中使用,因此该变量是在堆上而不是在堆栈上分配的。通常,您可以返回指向局部变量的指针,并且由于转义分析,编译器会在堆上分配该变量,例如:type X struct {&nbsp; &nbsp;...}func NewX() *X {&nbsp; return &X{}}这是一种常见的类似构造函数的模式来初始化结构。这段代码相当于:func NewX() *X {&nbsp; return new(X)}在您的程序中,count:=0声明相当于:count:=new(int)*count=0并且 goroutine 保持一个指向count. 对finished.

繁星点点滴滴

函数退出后,goroutine如何从调用函数访问局部变量?任何符合 Go 语言规范的编译器都可以使用其作者选择实现的任何技术,例如将所有变量放在堆上,根本不释放内存,使用引用计数或仅将一些变量放在堆栈上。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go