参数是怎么传给 goroutine 的
go version: 1.16
文章内容接之前的 variable shadowing
做了一些延伸,在批量创建 goroutine
时,避免不了参数传递,通常的做法如下:
for i := 0; i < 10; i++ { |
其实也可以通过 variable shadowing
来解决,这两种方法达到的效果是一样的,如下:
for i := 0; i < 10; i++ { |
疑问
由此,我产生了一个疑问,参数是如何传递给 goroutine
的。
寻找答案
我们使用上述代码调试,在源码中寻找答案,其实关键的代码就这两行:
func newproc(siz int32, fn *funcval) { |
栈是由高地址向低地址增长的,有时候不好确定这个增长方向,因为 x86 代码和 go 的汇编存在差异,区分不好应该是从左往右看,还是从右往左,既然这该死的脑子记不住,就找到一个窍门,每次先找 sub 或者 add 指令,通过这种方式来区分从哪边看起。
还有一点需要我们注意的就是,如何理解 go func(){}()
的这个函数地址与参数位置的关系,是谁把参数放在了与这个函数位置挨着的地方?为什么要挨着放在别的地方行不行?常规情况下,函数的参数传递形式如下图所示:
这里涉及到调用规约的内容,可以参考曹大的文章(文末引路),主要讲的就是函数间参数传递的方式以及返回值放在哪里等。
查看创建 goroutine 的源码,注释中有关于调用规约的描述:
// The stack layout of this call is unusual: it assumes that the |
首先提到的就是这里的的栈传参形式与平常是不一样的,这里的 fn
指的就是 go 关键字后面的 func,&fn
的意思就是 fn 函数的地址(一开始以为在执行这个取址操作后..)。
在 fn
函数地址后面,就是传递给它的参数。有意思的地方来了,goroutine 的参数是借用 newproc 传递给了 fn 函数,但是在 newproc 函数签名中只有两个参数,如下:
func newproc(siz int32, fn *funcval) { |
这里通过汇编代码验证下,我们增加传递给 goroutine 参数,加到 3 个:
# 第一个参数 siz 代表所有参数占用的字节数 |
虽然计算机的世界没有 magic,但是感觉是真 tmd 神奇。
最后在创建新 goroutine 的时候,把参数拷贝到当前 goroutine 的栈地址空间。分析的过程没什么难度,死记硬背并不是什么秘诀,掌握寻找答案的方法更重要。
最后,关于调用规约的相关内容,指路[ 曹大博客 ]。
本文标题:参数是怎么传给 goroutine 的
文章作者:bqyang
发布时间:2022-04-30
最后更新:2023-04-13
原始链接:https://bqyang.top/2022/language/golang/go-params-to-goroutine/
版权声明:The author owns the copyright, please indicate the source reproduced.