表盘厂家
免费服务热线

Free service

hotline

010-00000000
表盘厂家
热门搜索:
行业资讯
当前位置:首页 > 行业资讯

看一看:一篇学会Go并发等待

发布时间:2022-03-24 19:35:25 阅读: 来源:表盘厂家
一篇学会Go并发等待 作者:小熊 2021-07-16 22:43:10 开发 后端 关于 goroutine stack size(栈内存大小) 官方的文档 中所述,1.2 之前最小是4kb,在1.2 变成8kb,并且可以使用SetMaxStack 设置栈最大大小没有房产证拆迁怎么办。 上节答疑

上一节有读者问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 执行完成再接着往下执行,该怎么办?

  1. funcsay(sstring){
  2. fori:=0;i<3;i++{
  3. time.Sleep(100*time.Millisecond)
  4. fmt.Println(s)
  5. }
  6. }
  7. funcmain(){
  8. gosay("helloworld")
  9. fmt.Println("over!")
  10. }

输出 over! , 主线程没有等待

使用 Sleep 等待
  1. funcmain(){
  2. gosay("helloworld")
  3. time.Sleep(time.Second*1)
  4. fmt.Println("over!")
  5. }

运行修改后的程序,结果如下:

  1. helloworld
  2. helloworld
  3. helloworld
  4. over!

结果符合预期,但是太 low 了,我们不知道实际执行中应该等待多长时间,所以不能接受这个方案!

发送信号
  1. funcmain(){
  2. done:=make(chanbool)
  3. gofunc(){
  4. fori:=0;i<3;i++{
  5. time.Sleep(100*time.Millisecond)
  6. fmt.Println("helloworld")
  7. }
  8. done<-true
  9. }()
  10. <-done
  11. fmt.Println("over!")
  12. }

输出的结果和上面相同,也符合预期

这种方式不能处理多个协程,所以也不是优雅的解决方式。

WaitGroup

Golang 官方在 sync 包中提供了 WaitGroup 类型可以解决这个问题。其文档描述如下:

使用方法可以总结为下面几点:

  • 在父协程中创建一个 WaitGroup 实例拆迁里面有哪些套路,比如名称为:wg
  • 调用 wg.Add(n) ,其中 n 是等待的 goroutine 的数量
  • 在每个 goroutine 运行的函数中执行 defer wg.Done()
  • 调用 wg.Wait() 阻塞主逻辑
  • 直到所有 goroutine 执行完成。
  1. funcmain(){
  2. varwgsync.WaitGroup
  3. wg.Add(2)
  4. gosay2("hello",&wg)
  5. gosay2("world",&wg)
  6. fmt.Println("over!")
  7. wg.Wait()
  8. }
  9. funcsay2(sstring,waitGroup*sync.WaitGroup){
  10. deferwaitGroup.Done()
  11. fori:=0;i<3;i++{
  12. fmt.Println(s)
  13. }
  14. }

输出,注意顺序混乱是因为并发执行

  1. hello
  2. hello
  3. hello
  4. over!
  5. world
  6. world
  7. world
小心缺陷

简短的例子,注意循环传入的变量用中间变量替代,防止闭包 bug

  1. funcerrFunc(){
  2. varwgsync房屋被强拆官司打赢是什么结果.WaitGroup
  3. sList:=[]string{"a","b"}
  4. wg.Add(len(sList))
  5. for_,d:=rangesList{
  6. gofunc(){
  7. deferwg.Done()
  8. fmt.Println(d)
  9. }()
  10. }
  11. wg.Wait()
  12. }

输出,可以发现全部变成了最后一个

  1. b
  2. b

父协程与子协程是并发的。父协程上的for循环瞬间执行完了,内部的协程使用的是d最后的值,这就是闭包问题。

解决方法当作参数传入

  1. funccorrectFunc(){
  2. varwgsync.WaitGroup
  3. sList:=[]string{"a","b"}
  4. wg.Add(len(sList))
  5. for_,d:=rangesList{
  6. gofunc(strstring){
  7. deferwg.Done()
  8. fmt.Println(str)
  9. }(d)
  10. }
  11. wg.Wait()
  12. }

输出

  1. b
  2. a

要留意 range 中的value有可能出现 1.7.3 有可能会遇到的坑!