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创建的通道句柄。

使用通道发送数据

1) 格式
通道的发送使用特殊的操作符<-,将数据通过通道发送的格式为:

通道变量 <- 值
//通道变量:通过make创建好的通道实例。
//值:可以是变量、常量、表达式或者函数返回值等。
//值的类型必须与ch通道的元素类型一致。如果值是空接口类型,可以存放任意格式的数据

2) 发送将持续阻塞直到数据被接收

把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。

使用通道接收数据

通道接收同样使用 <- 操作符,通道接收有如下特性:

1)通道的收发操作在不同的两个 goroutine 间进行。

由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。

2)接收将持续阻塞直到发送方发送数据。

如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。

3) 通道一次只能接收一个数据元素。

4)通道接收数据一共有下面4种写法

1) 阻塞接收数据

data := <-ch

执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
2) 非阻塞接收数据
使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:

data, ok := <-ch
//data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
//ok:表示是否接收到数据

非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行。
3) 接收任意数据,忽略接收的数据
阻塞接收数据后,忽略从通道返回的数据,格式如下:

<-ch

执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。例如因为所有 goroutine 在 main() 函数结束时会一同结束,可以在main函数结束前等待其他函数的goroutine
4) 循环接收
通道的数据接收可以借用 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]

猜你喜欢

转载自www.cnblogs.com/jiangxiangxiang/p/10897259.html
今日推荐