学习协程前,可以先了解进程,线程的相关内容
goroutine
Go语言中,没有线程概念,只有协程goroutine。
go语言的并发是由go自己决定,对于开发者是透明的,仅需编码时告知启动几个goroutine即可,其余均不关心
和线程相比:
- 协程更轻量,一个程序可以启动成千上万个goroutine
- 协程是被go runtime 调度执行,线程,由操作系统调度执行,。
启动协程:使用关键字go 启动
go function()
channel
解决多个协程之间的通信,主要通过使用channel通道
声明channel,使用make函数
// 声明一个channel变量,其数据都是string
ch := make(chan string)
关键字chan,表示channel类型,是一个集合类型,
chan的操作只有两种:
接收:获取chan中的值,操作符为<- chan
发送:向chan发送值,把值放在chan中,操作符为chan<-
channel类似是goroutine的一个管道,一个往管道里发送数据,另一个从管道中取数据,如果在管道中获取不到数据,则将等到取到数据为止
无缓冲channel
- 声明一个无容量的channel,只起到传输数据的作用
- 无缓冲channel的发送和接收操作时同时进行,也称为同步channel
有缓冲channel
指定channel容量大小,创建一个有缓冲channel
ch := make(chan int,5)
- 有缓冲channel的内部有一个缓冲队列
- 发送操作向队列尾部插入元素,队列满时则阻塞等待,直到有协程从队列中接收数据从而释放队列空间
- 接收操作从队列的头部获取元素并把它从队列中删除,队列为空时则阻塞等待,直到有协程向队列中发送数据
ch := make(chan int,6) cap(ch) // 获取chan 长度 close(ch) //关闭chan
- 单向channel
场景:限制一个channel只接收不发送,或者只发送不接收
select
类似switch,但又有些不同。select只能用于信道channel,所以在这里提及select
select的case后带的是channel的操作,而不是判断条件。
- select语句只能用于信道channel的读写操作
- select的case条件是并发执行,会先执行操作成功的那个case先执行,如果多个同时返回时,则随机选择一个执行
- 如果case条件语句中,存在通道值为nil的读写操作,则该分支会被忽略
- 对于空select{},会引起死锁
对于for的select{},可能会引起cpu占用过高的问
ch := make(chan int , 10) for i := 0; i < size; i++ { ch <- 1 } select { case 1==1: // 此处运行时会报错 fmt.Println("equal") case v:= <-ch fmt.Print(v) default: fmt.Println("none") } select { case 1==1: // 此处运行时会报错 fmt.Println("equal") case v:= <-ch fmt.Print(v) case b:= <-ch fmt.Print(b) default: fmt.Println("none") }