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

问题描述

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()时,为什么countmu这样的局部变量不会超出范围?为什么我们仍未完成未完成的go例程,正确地更新了count变量?

解决方法

是“转义分析”的结果。编译器意识到变量count逃逸了函数main_helper,因为它在goroutine中使用,因此该变量分配在堆上而不是堆栈上。通常,您可以返回指向局部变量的指针,并且由于进行转义分析,编译器会在堆上分配该变量,例如:

type X struct {
   ...
}

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

这是一个类似于构造函数的通用模式,用于初始化结构。此代码等效于:

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

在您的程序中,count:=0声明等效于:

count:=new(int)
*count=0

,goroutine保持指向count的指针。与finished相同。

,

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

任何符合Go语言规范的编译器都可以使用其作者选择实施的任何技术,例如将所有变量放在堆上,而不释放内存,使用引用计数或仅将某些变量放在堆栈上。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...