go语言并发
goroutine
goroutine格式
- 为一个普通函数创建 goroutine 的写法如下:
go funcName(参数列表)
- 为一个匿名函数创建goroutine的写法如下
go func(参数列表){
函数体
}(参数列表)
goroutine创建流程
-
Go 程序从 main 包的 main() 函数开始,在程序启动时,运行时(runtime)会默认为 main() 函数创建一个默认的 goroutine。
-
在 main() 函数的 goroutine 中执行到 go funcName语句时,归属于 funcName函数的 goroutine 被创建。
-
此时,main() 继续执行,funcName函数开始在自己的 goroutine 中执行,两个 goroutine 通过 Go 程序的调度机制同时运作。
channel
-
使用 go 关键字创建 goroutine 时,被调用函数的返回值会被忽略。
-
如果需要在 goroutine 中返回数据,需要通过通道把数据从 goroutine 中作为返回值传出。
-
在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。
声明通道类型
通道的元素类型就是在其内部传输的数据类型,声明如下:
var 通道变量 chan 通道类型
//通道类型:通道内的数据类型。
//通道变量:保存通道的变量。
chan 类型的空值是 nil,声明后需要配合 make 后才能使用。
创建通道
通道是引用类型,需要使用 make 进行创建,格式如下:
通道实例 := make(chan 数据类型)
//数据类型:通道内传输的元素类型。
//通道实例:通过make创建的通道句柄。
使用通道发送数据
- 格式
通道的发送使用特殊的操作符<-,将数据通过通道发送的格式为:
通道变量 <- 值
//通道变量:通过make创建好的通道实例。
//值:可以是变量、常量、表达式或者函数返回值等。
//值的类型必须与ch通道的元素类型一致。如果值是空接口类型,可以存放任意格式的数据
- 发送将持续阻塞直到数据被接收
把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。
使用通道接收数据
通道接收同样使用 <- 操作符,通道接收有如下特性:
- 通道的收发操作在不同的两个 goroutine 间进行。
由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。
- 接收将持续阻塞直到发送方发送数据。
如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。
-
通道一次只能接收一个数据元素。
-
通道接收数据一共有下面4种写法
-
阻塞接收数据
data := <-ch //执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
-
非阻塞接收数据
使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
data, ok := <-ch //data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。 //ok:表示是否接收到数据
非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行。
-
接收任意数据,忽略接收的数据
阻塞接收数据后,忽略从通道返回的数据,格式如下:
<-ch
执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。例如因为所有 goroutine 在 main() 函数结束时会一同结束,可以在main函数结束前等待其他函数的goroutine
-
循环接收
通道的数据接收可以借用 for range 语句进行多个元素的接收操作,格式如下:
for data := range ch {}
通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过for遍历获得的变量只有一个,即上面例子中的 data。
-
单向通道
Go 的通道可以在声明时约束其操作方向,如只发送或是只接收。这种被约束方向的通道被称做单向通道。
单向通道的声明
只能发送的通道类型为 chan<-,格式如下:
var 通道实例 chan<- 元素类型
只能接收的通道类型为 <-chan,格式如下:
var 通道实例 <-chan 元素类型
创建单向通道
通道实例 := make(<-chan 数据类型)
通道实例 := make(chan<- 数据类型)
带缓冲的通道Buffered Channels
在无缓冲通道的基础上,为通道增加一个有限大小的存储空间形成带缓冲通道。
带缓冲通道在发送时无需等待接收方接收即可完成发送过程,并且不会发生阻塞,只有当存储空间满时才会发生阻塞。
同理,如果缓冲通道中有数据,接收时将不会发生阻塞,直到通道中没有数据可读时,通道将会再度阻塞。
创建带缓冲的通道
通道实例 := make(chan 通道类型, 缓冲大小)
//缓冲大小:决定通道最多可以保存的元素数量。
go语言通道的多路复用
Go里面提供了一个关键字select监听channel上的数据流动。
select默认是阻塞的,只有当监听的channel中有发送或接收可以进行时才会运行,当多个channel都准备好的时候,select是随机的选择一个执行的。
在select里面还有default语法,select其实就是类似switch的功能,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。
select{ case 操作1: 响应操作1 case 操作2: 响应操作2 … default: 没有操作情况 }
go语言关闭通道后继续使用通道
格式
close(ch)
被关闭的通道不会被置为 nil。如果对已经关闭的通道进行发送,将会触发panic。
从已经关闭的通道接收数据或者正在接收数据时,将会接收到通道类型的零值,然后停止阻塞并返回。
[参考资料http://c.biancheng.net/view/99.html]