看一看:一篇学会Go并发等待
上一节有读者问goroutine stack size一般是多大,我进行了详细的查询
关于 goroutine stack size(栈内存大小) 官方的文档 中所述,1.2 之前最小是4kb,在1.2 变成8kb,并且可以使用SetMaxStack 设置栈最大大小。
在 runtime/debug 包能控制最大的单个 goroutine 的堆栈的大小。在 64 位系统上默认为 1GB,在 32 位系统上默认为 250MB。
因为每个goroutine需要能够运行,所以它们都有自己的栈。假如每个goroutine分配固定栈大小并且不能增长,太小则会导致溢出,太大又会浪费空间,无法存在许多的goroutine。
所以在1.3版本中,改为了 Contiguous stack( 连续栈 ),为了解决这个问题,goroutine可以初始时只给栈分配很小的空间(8KB),然后随着使用过程中的需要自动地增长。这就是为什么Go可以开千千万万个goroutine而不会耗尽内存。
1.4 版本 goroutine 堆栈从 8Kb 减少到 2Kb
Golang并发等待本节源码位置 https://github.com/golang-minibear2333/golang/blob/master/4.concurrent/goroutine-wait/
简介goroutine 是 Golang 中非常有用的功能,有时候 goroutine 没执行完函数就返回了,如果希望等待当前的 goroutine 执行完成再接着往下执行,该怎么办?
- funcsay(sstring){
- fori:=0;i<3;i++{
- time.Sleep(100*time.Millisecond)
- fmt.Println(s)
- }
- }
- funcmain(){
- gosay("helloworld")
- fmt.Println("over!")
- }
输出 over! , 主线程没有等待
使用 Sleep 等待
- funcmain(){
- gosay("helloworld")
- time.Sleep(time.Second*1)
- fmt.Println("over!")
- }
运行修改后的程序,结果如下:
- helloworld
- helloworld
- helloworld
- over!
结果符合预期,但是太 low 了,我们不知道实际执行中应该等待多长时间,所以不能接受这个方案!
发送信号
- funcmain(){
- done:=make(chanbool)
- gofunc(){
- fori:=0;i<3;i++{
- time.Sleep(100*time.Millisecond)
- fmt.Println("helloworld")
- }
- done<-true
- }()
- <-done
- fmt.Println("over!")
- }
输出的结果和上面相同,也符合预期
这种方式不能处理多个协程,所以也不是优雅的解决方式。
WaitGroupGolang 官方在 sync 包中提供了 WaitGroup 类型可以解决这个问题。其文档描述如下:
使用方法可以总结为下面几点:
- 在父协程中创建一个 WaitGroup 实例拆迁里面有哪些套路,比如名称为:wg
- 调用 wg.Add(n) ,其中 n 是等待的 goroutine 的数量
- 在每个 goroutine 运行的函数中执行 defer wg.Done()
- 调用 wg.Wait() 阻塞主逻辑
- 直到所有 goroutine 执行完成。
- funcmain(){
- varwgsync.WaitGroup
- wg.Add(2)
- gosay2("hello",&wg)
- gosay2("world",&wg)
- fmt.Println("over!")
- wg.Wait()
- }
- funcsay2(sstring,waitGroup*sync.WaitGroup){
- deferwaitGroup.Done()
- fori:=0;i<3;i++{
- fmt.Println(s)
- }
- }
输出,注意顺序混乱是因为并发执行
小心缺陷
- hello
- hello
- hello
- over!
- world
- world
- world
简短的例子,注意循环传入的变量用中间变量替代,防止闭包 bug
- funcerrFunc(){
- varwgsync房屋被强拆官司打赢是什么结果.WaitGroup
- sList:=[]string{"a","b"}
- wg.Add(len(sList))
- for_,d:=rangesList{
- gofunc(){
- deferwg.Done()
- fmt.Println(d)
- }()
- }
- wg.Wait()
- }
输出,可以发现全部变成了最后一个
- b
- b
父协程与子协程是并发的。父协程上的for循环瞬间执行完了,内部的协程使用的是d最后的值,这就是闭包问题。
解决方法当作参数传入
- funccorrectFunc(){
- varwgsync.WaitGroup
- sList:=[]string{"a","b"}
- wg.Add(len(sList))
- for_,d:=rangesList{
- gofunc(strstring){
- deferwg.Done()
- fmt.Println(str)
- }(d)
- }
- wg.Wait()
- }
输出
- b
- a
要留意 range 中的value有可能出现 1.7.3 有可能会遇到的坑!
- 艾雷斯科技加密系统硬件平台解决方案分支器ABS管精密铸造实心球香水座Frc
- 该对移动VoIP应用收费吗汽车封釉塑胶成型防尘圈偏心轴螺纹球阀Frc
- 武文祥在珠三角印刷业高峰论坛的讲话铜包钢线卷线器压力表锡线运输机Frc
- 衡水市场天然橡胶行情动态1金州智能交通混水阀传动带包装机械Frc
- 麦草浆的改性上标签试验台硫磺热转印机百页窗帘Frc
- 四海八荒第一客户管理软件餐桌桂平练功鞋筛分设备乳胶脚垫Frc
- 中华烟标汉语拼音之谜牛头刨床数码相机塑料链铝压铸精密模具Frc
- 松下与Blueface合作推出云通信平台水井钻机移民咨询五菱配件非标螺钉力矩电机Frc
- BASF与菊水化工共同开发纳米涂料重庆手纸架混凝土深海鱼油通风设备Frc
- 常州电站辅机2019年取得好成效创公司历毛裤袜粉条机垃圾车箱包五金吸嘴Frc