Golang关键字go
1 结果为什么不是10个10?
func main() {
runtime.GOMAXPROCS(1)
for i := 0; i < 10; i++ {
go print(i, ",")
}
runtime.Gosched()
time.Sleep(time.Second)
}//9,0,1,2,3,4,5,6,7,8和0,1,2,3,4,5,6,7,8,9
2 从源码入手
runtime. GOMAXPROCS(1) 强行指定了只创建一个 P 来处理并发 这使得例子中的 10 个 goroutine 会是串行的
2.1 知其然 知其所以然
小心处理 for 循环中变量
func main() {
runtime.GOMAXPROCS(1)
for i := 0; i < 10; i++ {
go func() {
print(i, ",")
}()
}
runtime.Gosched()
time.Sleep(time.Second)
}//10,10,10,10,10,10,10,10,10,10 显示 10个10
区别仅仅是 go 关键字后的函数有区别 go 是创建了 goroutine
但是对于计算机而言 goroutine 只是语言封装的语法糖而已 对于计算机依旧是识别指令及内存里的值
那么 goroutine 在被创建后 留给计算机是什么样的内存布局(数据结构)呢?
在 \Go\src\runtime\runtime2.go:338 (type g struct) 定义
编译器会把 go 后面的方法和参数打包在 goroutine 里
type g struct {
...
FuncVal* fnstart; // goroutine运行的函数
void* param; // 用于传递参数 睡眠时其它goroutine设置param 唤醒时此 goroutine 可以获取
...
}
也就是说 运行到 go 的时候 编译器就已经把 goroutine 需要运行的参数与方法都保存了
demo 1 来说就是保存了 { println, current_i }
demo 2 保存的是 { main.func_xxx, nil } 并没有把 i 传入到匿名函数中 但是引用的时候并没有发生 panic
为什么?因为这里有内存逃逸 这也是为什么 demo 2 会输出 10 个 10 的原因
编译器会把 go 后面跟着的参数与函数都打包成了对象 等待系统调度
2.2 为什么
虽说 GOMAXPROCS 为 1 导致所有的 goroutine 变成了串行 结果也不是 0 ~ 9 为什么 ?
go 在把 goroutine 放入队列(go sched 内容会有另外的篇幅来说明) 时 还做了一件很特别的事 proc:4799 (next) 代码
if next {
retryNext:
oldnext := _p_.runnext
if !_p_.runnext.cas(oldnext, guintptr(unsafe.Pointer(gp))) {
goto retryNext
}
if oldnext == 0 {
return
}
// Kick the old runnext out to the regular run queue.
gp = oldnext.ptr()
} 意思是 go 会把每个 P 所管理的最后一个 goroutine 放入 next 位置 为什么 ?
go 设计认为或 有过测试 如果一个 P 的 goroutine 队列在顺序执行的时候 因为 go sched 会有很多抢占或者调度 那么从被执行的概率上来分析的话 放入一个 next 位置可使得每个 goroutine 的执行概率是相当的 这个 next 位置也就解释了 demo 1 的结果为什么会是 9 0~8 到这已经说明了前面的问题 但是 demo 中出现了
runtime.Gosched()// 让出CPU时间片 让出当前goroutine的执行权限 调度器安排其它等待的任务运行 并在下次某个时候从该位置恢复执行
这一行代码。
如果注释掉结果会怎样?//0,1,2,3,4,5,6,7,8,9
如果把这一行换成 runtime.Goexit() 或者 os.Exit(0) 又会是如何呢?
1 结果为什么不是10个10?
func main() {
runtime.GOMAXPROCS(1)
for i := 0; i < 10; i++ {
go print(i, ",")
}
runtime.Gosched()
time.Sleep(time.Second)
}//9,0,1,2,3,4,5,6,7,8和0,1,2,3,4,5,6,7,8,9
2 从源码入手
runtime. GOMAXPROCS(1) 强行指定了只创建一个 P 来处理并发 这使得例子中的 10 个 goroutine 会是串行的
2.1 知其然 知其所以然
小心处理 for 循环中变量
func main() {
runtime.GOMAXPROCS(1)
for i := 0; i < 10; i++ {
go func() {
print(i, ",")
}()
}
runtime.Gosched()
time.Sleep(time.Second)
}//10,10,10,10,10,10,10,10,10,10 显示 10个10
区别仅仅是 go 关键字后的函数有区别 go 是创建了 goroutine
但是对于计算机而言 goroutine 只是语言封装的语法糖而已 对于计算机依旧是识别指令及内存里的值
那么 goroutine 在被创建后 留给计算机是什么样的内存布局(数据结构)呢?
在 \Go\src\runtime\runtime2.go:338 (type g struct) 定义
编译器会把 go 后面的方法和参数打包在 goroutine 里
type g struct {
...
FuncVal* fnstart; // goroutine运行的函数
void* param; // 用于传递参数 睡眠时其它goroutine设置param 唤醒时此 goroutine 可以获取
...
}
也就是说 运行到 go 的时候 编译器就已经把 goroutine 需要运行的参数与方法都保存了
demo 1 来说就是保存了 { println, current_i }
demo 2 保存的是 { main.func_xxx, nil } 并没有把 i 传入到匿名函数中 但是引用的时候并没有发生 panic
为什么?因为这里有内存逃逸 这也是为什么 demo 2 会输出 10 个 10 的原因
编译器会把 go 后面跟着的参数与函数都打包成了对象 等待系统调度
2.2 为什么
虽说 GOMAXPROCS 为 1 导致所有的 goroutine 变成了串行 结果也不是 0 ~ 9 为什么 ?
go 在把 goroutine 放入队列(go sched 内容会有另外的篇幅来说明) 时 还做了一件很特别的事 proc:4799 (next) 代码
if next {
retryNext:
oldnext := _p_.runnext
if !_p_.runnext.cas(oldnext, guintptr(unsafe.Pointer(gp))) {
goto retryNext
}
if oldnext == 0 {
return
}
// Kick the old runnext out to the regular run queue.
gp = oldnext.ptr()
} 意思是 go 会把每个 P 所管理的最后一个 goroutine 放入 next 位置 为什么 ?
go 设计认为或 有过测试 如果一个 P 的 goroutine 队列在顺序执行的时候 因为 go sched 会有很多抢占或者调度 那么从被执行的概率上来分析的话 放入一个 next 位置可使得每个 goroutine 的执行概率是相当的 这个 next 位置也就解释了 demo 1 的结果为什么会是 9 0~8 到这已经说明了前面的问题 但是 demo 中出现了
runtime.Gosched()// 让出CPU时间片 让出当前goroutine的执行权限 调度器安排其它等待的任务运行 并在下次某个时候从该位置恢复执行
这一行代码。
如果注释掉结果会怎样?//0,1,2,3,4,5,6,7,8,9
如果把这一行换成 runtime.Goexit() 或者 os.Exit(0) 又会是如何呢?
尊贵的董事大人
英文标题不为空时 视为本栏投稿
需要关键字 描述 英文标题