go语言并发

go语言并发

goroutine

goroutine格式

  • 为一个普通函数创建 goroutine 的写法如下:
go funcName(参数列表)
  • 为一个匿名函数创建goroutine的写法如下
go func(参数列表){
函数体
}(参数列表)

goroutine创建流程

  1. Go 程序从 main 包的 main() 函数开始,在程序启动时,运行时(runtime)会默认为 main() 函数创建一个默认的 goroutine。

  2. 在 main() 函数的 goroutine 中执行到 go funcName语句时,归属于 funcName函数的 goroutine 被创建。

  3. 此时,main() 继续执行,funcName函数开始在自己的 goroutine 中执行,两个 goroutine 通过 Go 程序的调度机制同时运作。

channel

  • 使用 go 关键字创建 goroutine 时,被调用函数的返回值会被忽略。

  • 如果需要在 goroutine 中返回数据,需要通过通道把数据从 goroutine 中作为返回值传出。

  • 在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。

声明通道类型

通道的元素类型就是在其内部传输的数据类型,声明如下:

var 通道变量 chan 通道类型
//通道类型:通道内的数据类型。
//通道变量:保存通道的变量。

chan 类型的空值是 nil,声明后需要配合 make 后才能使用。

创建通道

通道是引用类型,需要使用 make 进行创建,格式如下:

通道实例 := make(chan 数据类型)
//数据类型:通道内传输的元素类型。
//通道实例:通过make创建的通道句柄。

使用通道发送数据

  • 格式

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

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

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

使用通道接收数据

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

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

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

  1. 接收将持续阻塞直到发送方发送数据。

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

  1. 通道一次只能接收一个数据元素。

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

    1. 阻塞接收数据

      data := <-ch
      //执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
      
    2. 非阻塞接收数据

      使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:

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

      非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行。

    3. 接收任意数据,忽略接收的数据

    阻塞接收数据后,忽略从通道返回的数据,格式如下:

    <-ch
    

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

    1. 循环接收

      通道的数据接收可以借用 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]

posted on 2019-05-20 23:20  j_x_x  阅读(197)  评论(0编辑  收藏  举报

导航