Goroutine

Go语言的并发执行体称为goroutine。Go语言通过go关键字来启动一个goroutine,但是go关键字后面必须跟一个函数,不能是语句或其他,函数的返回值将被忽略。

Goroutine启动的两种方式:

1.通过匿名函数方式。

2.通过有名函数方式。


goroutine有如下特性:

  1. Go的执行是非阻塞的,不会等待。
  2. Go后面的函数的返回值会被忽略。调度器不能保证多个goroutine的执行次序。
  3. 没有父子goroutine的概念,所有的goroutine是平等地被调度和执行的。
  4. Go程序在执行时会单独为main函数创建一个goroutine,遇到其他go关键字时再去创建其他的goroutine。
  5. 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进入等待时期,等待信号到来,然后重新启动。

例:

运行结果: