Go 并发——实现协程同步的几种方式

前言

Java 中有一系列的线程同步的方法,go 里面有 goroutine(协程),先看下下面的代码执行的结果是什么呢?

package main

import (
    "fmt"
)

func main() {
    go func() {
        fmt.Println("Goroutine 1")
    }()

    go func() {
        fmt.Println("Goroutine 2")
    }()
}

执行以上代码很可能看不到输出。

因为有可能这两个协程还没得到执行,主协程就已经结束了,而主协程结束时会结束所有其他协程,所以导致代码运行的结果什么都没有。

估计不少新接触 go 的童鞋都会对此郁闷?,可能会问那么该如何等待主协程中创建的协程执行完毕之后再结束主协程呢?

下面说几种可以解决的方法:

Sleep 一段时间

在 main 方法退出之前 sleep 一段时间就可能会出现结果了,如下代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    go func() {
        fmt.Println("Goroutine 1")
    }()

    go func() {
        fmt.Println("Goroutine 2")
    }()

    time.Sleep(time.Second * 1) // 睡眠1秒,等待上面两个协程结束
}

这两个简单的协程执行消耗的时间很短的,所以你会发现现在就有结果出现了。

Goroutine 1
Goroutine 2

为什么上面我要说 “可能会出现” ?

因为 sleep 这个时间目前是设置的 1s,如果我这两个协程里面执行了很复杂的逻辑操作(时间大于 1s),那么就会发现依旧也是无结果打印出来的。

那么就可以发现这种方式得到问题所在了:我们无法确定需要睡眠多久

上面那种方式有问题,go 里面其实也可以用管道来实现同步的。

管道实现同步

那么用管道怎么实现同步呢?show code:

package main

import (
    "fmt"
)

func main() {

    ch := make(chan struct{})
    count := 2 // count 表示活动的协程个数

    go func() {
        fmt.Println("Goroutine 1")
        ch <- struct{}{} // 协程结束,发出信号
    }()

    go func() {
        fmt.Println("Goroutine 2")
        ch <- struct{}{} // 协程结束,发出信号
    }()

    for range ch {
        // 每次从ch中接收数据,表明一个活动的协程结束
        count--
        // 当所有活动的协程都结束时,关闭管道
        if count == 0 {
            close(ch)
        }
    }
}

这种方式是一种比较完美的解决方案, goroutine / channel 它们也是在 go 里面经常搭配在一起的一对。

sync.WaitGroup

其实 go 里面也提供了更简单的方式 —— 使用 sync.WaitGroup。

WaitGroup 顾名思义,就是用来等待一组操作完成的。WaitGroup 内部实现了一个计数器,用来记录未完成的操作个数,它提供了三个方法:

  • Add() 用来添加计数
  • Done() 用来在操作结束时调用,使计数减一
  • Wait() 用来等待所有的操作结束,即计数变为 0,该函数会在计数不为 0 时等待,在计数为 0 时立即返回

继续 show code:

package main

import (
    "fmt"
    "sync"
)

func main() {

    var wg sync.WaitGroup

    wg.Add(2) // 因为有两个动作,所以增加2个计数
    go func() {
        fmt.Println("Goroutine 1")
        wg.Done() // 操作完成,减少一个计数
    }()

    go func() {
        fmt.Println("Goroutine 2")
        wg.Done() // 操作完成,减少一个计数
    }()

    wg.Wait() // 等待,直到计数为0
}

你会发现也是可以看到运行结果的,是不是发现这种方式是很简单的。

总结

多看别人写的代码;多想想为啥要这样写;多查自己不理解的地方;多写 demo 测试;多写文章总结。

关注我

zhisheng

本文地址为:http://www.54tianzhisheng.cn/2018/08/30/go-sync/ ,转载请注明原文出处!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhisheng_blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值