前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个有意思的go routine案例

一个有意思的go routine案例

作者头像
AsiaYe
发布2022-12-07 09:09:45
1910
发布2022-12-07 09:09:45
举报
文章被收录于专栏:DBA随笔DBA随笔

Golang 一个有意思的go routine案例

今天我们看一个有意思的go routine案例,从而了解golang中的sync包的waitgroup用法。

01

一个简单案例

我们写一个简单的for循环,循环体里面写上go routine,启动多个goroutine来打印循环变量i

代码语言:javascript
复制
func main() {
    for i := 0; i < 200; i++ {
        go fmt.Println("routine:", i)
        fmt.Println(i)
    }
}

上述案例可以得到下面输出:

代码语言:javascript
复制
...省略
21
22
routine: 0
routine: 3
routine: 1
routine: 13
23
24
...省略
198
199
routine: 189
routine: 198
routine: 188

...

由于输出了200个数字,太长了占满页面,结果的前后我省略了一部分。

上面的例子不难看出来:

1、因为go routine是协程,所以存在这个routine的先后顺序无法保证,可能后面的循环先输出。例如routine3出现在routine1的前面。

2、go routine的运行需要时间,循环到第22次的时候,routine0的结果才输出来。循环结束的时候,还有一些go routine没有执行完。

为了保证go routine全部执行完毕并输出,我们往往会使用sleep 函数阻塞主程序main,从而等待go routine运行。上面的程序会改成:

代码语言:javascript
复制
func main() {
    for i := 0; i < 200; i++ {
        go fmt.Println("routine:", i)
        fmt.Println(i)
    }
    time.Sleep(time.Second * 1)
}
...

上述程序非常简单,是一个简单循环,循环体中只有打印语句,没有其他的语句。

但是如果循环中的逻辑特别多的时候,我们应该sleep多久,才能保证所有的go routine执行完毕呢???答案是不确定

02

利用channel

为了解决上述问题,我们引入了管道channel。channel默认是阻塞的。当数据被发送到channel时会发生阻塞,直到有其他go routine从该channel中读取数据。当从channel读取数据时,读取也会被阻塞,直到其他go routine将数据写入该channel。

代码语言:javascript
复制
func main(){
    c := make(chan bool, 200)
    for i := 0; i < 200; i++ {
        go func(i int) {
            fmt.Println(i)
            c <- true
        }(i)
    }

    for i := 0; i < 200; i++ {
        <-c
    }
}

上述代码中,我们利用管道天然的阻塞特性,先初始化一个channel,channel中传递bool类型的值,然后运行go routine,并在go routine结束的时候,将true塞入channel。

在第一个循环结束之后(并非go routine结束),我们开始遍历这个channel,从channel中吐出来200个bool类型的值,如果不够200个,程序将天然阻塞,这就能够保证,所有的go routine都被执行完了。

channel的方法能够解决我们上述的time.sleep时间不确定的问题。

但是channel有一个缺点,就是比较耗费内存。假设我们的循环终止条件上限是10w或者100w,那么我们不得不申请同样大小的channel。

03

利用sync包工具sync.WaitGroup

除了channel之外,还可以使用sync.WaitGroup来解决这个问题。

WaitGroup 对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait() 用来控制计数器的数量。其中:

Add(n) 把计数器设置为n ,

Done() 每次把计数器执行减一操作 ,

wait() 会阻塞代码的运行,直到计数器的值减为0。

代码语言:javascript
复制
func main() {
    wg := sync.WaitGroup{}
    wg.Add(200)
    for i := 0; i < 200; i++ {
        go func(i int) {
            fmt.Println(i)
            wg.Done()
        }(i)
    }
    wg.Wait()
} 

我们遵照上面的描述,先设置计数器为200,然后go func中执行Done,对计数器进行减一操作,然后最后利用wait函数,捕获计数器的值为0的时候,结束程序。

相比较而言,sync.WaitGroup工具会更加轻量。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-12-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DBA随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Golang 一个有意思的go routine案例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档