Golang 并发 - 协程

并发与并行

提到并发,相信大家还听过另一个概念 – 并行。首先介绍下这两者之间的区别,再来讲 Go 语言的并发。

并行其实很好理解,就是同时执行的意思,在某一时间点能够执行多个任务。
想达到并行效果,最简单的方式就是借助多线程或多进程,这样才可在同一时刻执行多个任务。单线程是永远无法达到并行状态的。
并发是在某一时间段内可以同时处理多个任务。我们通常会说程序是并发设计的,也就是说它允许多个任务同时执行,这个同时指的就是一段时间内。单线程中多个任务以间隔执行实现并发。
可以说,多线程或多进程是并行的基础,但单线程也通过协程实现了并发。

举个常见的例子,一台单核电脑可以下载、听音乐,实际上这两个任务是这样执行的,只不过这两个任务切换时间短,给人的感觉是同时执行的。
在这里插入图片描述
一台多核电脑的任务执行就是像下面这种图显示的一样:
在这里插入图片描述
可以看到,同一时刻能执行多个任务。这种任务执行方式才是真正的并行。

Go 通过协程实现并发,协程之间靠信道通信,本篇文章先给大家介绍协程的使用,后面再写信道。

协程

协程(Goroutine)可以理解成轻量级的线程,但与线程相比,它的开销非常小。因此,Go 应用程序通常能并发地运行成千上万的协程。
Go 创建一个协程非常简单,只要在方法或函数调用之前加关键字 go 即可。

func printHello() {
    fmt.Println("hello world goroutine")
}

func main() {
    go printHello()    // 创建了协程
    fmt.Println("main goroutine")
}

输出:

main goroutine

上面代码,第 6 行使用 go 关键字创建了协程,现在有两个协程,新创建的协程和主协程。printHello() 函数将会独立于主协程并发地执行。
是的,你没有看错,程序的输出就是这样,不信?你可以实际运行下程序。你惊讶的是,printHello() 函数为什么没有输出,到底发生了什么?
当协程创建完毕之后,主函数立即返回继续执行下一行代码,不像函数调用,需要等函数执行完成。主协程执行完毕,程序便退出,printHello 协程随即也退出,便不会有输出。

修改下代码:

func printHello() {
    fmt.Println("hello world goroutine")
}

func main() {
    go printHello()
    time.Sleep(1*time.Second)
    fmt.Println("main goroutine")
}

协程创建完成之后,main 协程先休眠 1s,预留给 printHello 协程执行的时间,所以这次输出:

hello world goroutine
main goroutine

创建多个协程

上一节就提到,可以创建多个协程,来看下例子:

func printNum() {
    for i := 1; i <= 5; i++ {
        time.Sleep(20 * time.Millisecond)
        fmt.Printf("%d ", i)
    }
}
func printChacter() {
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(40 * time.Millisecond)
        fmt.Printf("%c ", i)
    }
}
func main() {
    go printNum()
    go printChacter()
    time.Sleep(3*time.Second)
    fmt.Println("main terminated")
}

上面的代码,除主协程之外,新创建了两个协程:printNum 协程和 printChacter 协程。printNum 协程每隔 20 毫秒输出 5 个数,printChacter 协程每隔 40 毫秒输出字母。主协程创建完这两个协程之后休眠 1s,等待其他协程执行完成。
程序输出(你的输出可能跟我的不一样):

1 a 2 3 b 4 5 c d e main terminated

在这里插入图片描述
话说回来,通过加 time.Sleep() 函数等待协程执行完成是一种“黑科技”。在实际生产环境中,不管我们是否知道其他协程执行完成需要多少时间,都不能在主协程中添加随机睡眠调用等待其他协程执行完成。那怎么办?Go 给我们提供了信道,当协程执行完毕,能够通知到主协程,还能够实现协程间通信。下节课我们来讨论一下。

匿名协程

在函数那篇文章讲过,存在匿名函数,通过关键字 go 调用匿名函数就是匿名协程。我们修改之前的例子:

func main() {
    go func() {
        fmt.Println("hello world goroutine")
    }()
    time.Sleep(1*time.Second)
    fmt.Println("main goroutine")
}

输出结果跟之前的一样。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值