Goroutine
Go语言的并发执行体称为goroutine。Go语言通过go关键字来启动一个goroutine,但是go关键字后面必须跟一个函数,不能是语句或其他,函数的返回值将被忽略。
Goroutine启动的两种方式:
1.通过匿名函数方式。
2.通过有名函数方式。
goroutine有如下特性:
- Go的执行是非阻塞的,不会等待。
- Go后面的函数的返回值会被忽略。调度器不能保证多个goroutine的执行次序。
- 没有父子goroutine的概念,所有的goroutine是平等地被调度和执行的。
- Go程序在执行时会单独为main函数创建一个goroutine,遇到其他go关键字时再去创建其他的goroutine。
- Go 没有暴露 goroutine id 给用户,所以不能在一个goroutine里面显式地操作另一个goroutine,不过runtime 包提供了一些函数访问和设置 goroutine的相关信息。
Goroutine同步等待
当多个goroutine同时进行处理的时候,就会遇到同时抢占一个资源的情况(并发都会遇到的问题),所以我们希望某个goroutine等待另一个goroutine处理完某一个步骤之后才能继续。
chan
chan不仅可以用来goroutine之间的通信,也可以使goroutine同步完成协作。这点主要基于从channel取数据的时候,会阻塞当前goroutine这个特性。
通过无缓冲的通道实现goroutine之间的同步等待
例:
运行结果:
注意:
操作不同状态的chan会引发的三种行为:
Panic
(1)向已经关闭的通道写数据会导致panic。
(2)重复关闭相同的通道会导致panic。
阻塞
(1)向未初始化的通道写数据或读数据都会导致当前goroutine的永久阻塞。
(2)向缓冲区已满的通道写入数据会导致goroutine阻塞。
(3)读取没有数据的通道会导致goroutine阻塞。
非阻塞
(1)读取已经关闭的通道不会引发阻塞,而是会得到该通道元素类型的零值。
(2)向有缓冲且没有满的通道读或写不会引发阻塞。
sync.WaitGroup
Go中内置的sync包中也提供了多个goroutine同步的机制,主要是通过WaitGroup实现的。
WaitGroup的主要数据结构如下:
WaitGroup的三个方法:
例:
运行结果:
sync.Once
有的时候,我们启动多个相同goroutine,但是里面的某个操作我只希望被执行一次,这个时候我们可以使用sync.Once。
例:
运行结果:
sync.Cond
sync.Cond是用来控制某个条件下,goroutine进入等待时期,等待信号到来,然后重新启动。
例:
运行结果: