这是我参与「第五届青训营 」笔记创作活动的第4天
协程简介
一个进程包含多个线程,线程是操作系统运行调度的基本单位。但是线程太占资源,且线程的切换会导致用户态和内核态的频繁转换,调度开销大。
goroutine是go语言中的一个轻量级线程,即协程,更高效、轻便、开销小。可运行上千万个并发。一个线程包含多个协程。
开启协程
采用go关键字便可开启一个协程
func main() {
go thread1()
}
func thread1() {
for i := 0; i < 10; i++ {
fmt.Println("thread1打印", i)
}
}
运行上述代码我们会发现,控制台什么也没有输出,因为,当一个goroutine启动后,主线程不知道goroutine什么时候结束,所以主线程结束的时候,goroutine可能还没执行完毕,就随着主线程main()关闭而被强制关闭了。
channel
因此,goroutine通过channel的方式来传递消息。
var channel chan int
func transmitMessage(methodName string) {
for i := 0; i < 5; i++ {
fmt.Printf("方法%v打印:%v\n", methodName, i)
}
channel <- 0
}
func main() {
channel = make(chan int)
go transmitMessage("a")
go transmitMessage("b")
fmt.Println("start")
<-channel
<-channel
}
channel只能通过make方法来创建,并且指定channel的传值类型。向channel插入数据时channel <- 数据 ,从channel取出数据通过 <-channel
Gosched()
通过runtime.Gosched()可以使当前goroutine放弃当前CPU执行权,进入CPU调度的就绪队列。如下,协程之间交叉打印。
var channel chan int
func transmitMessage(methodName string, msg int) {
for i := 0; i < 5; i++ {
fmt.Printf("方法%v打印:%v\n", methodName, i)
runtime.Gosched()
}
channel <- msg
}
func main() {
channel = make(chan int)
go transmitMessage("a", 1)
go transmitMessage("b", 2)
fmt.Println("start")
<-channel
<-channel
}
select{}
当需要监听多个channel时,可使用select随机选择一个channel进行处理。
case <-ch1 : // 检测是否数据写入
case ch2 <- 1 : // 检测是否有数据可写
default:
// 如果以上都没有符合条件,那么进入default处理流程
}
随机选择一个channel读取,如下:
func main() {
ch1 := make(chan int, 1)
ch1 <- 1 //向通道ch1写入数据
ch2 := make(chan int, 1)
ch2 <- 2 //向通道ch2写入数据
select {
case <-ch1:
fmt.Println("ch1 read")
case <-ch2:
fmt.Println("ch2 read")
}
}
func main() {
select {
}
}
select空时,会引发死锁。
select{}应用:
典型应用,超时判断
//全局resChan来接受response,如果时间超过3S,
//resChan中还没有数据返回,则第二条case将执行
var returnChan = make(chan int)
// do request
func test() {
select {
case data := <-returnChan:
getData(data)
case <-time.After(time.Second * 3):
fmt.Println("请求超时")
}
}
func getData(data int) {
//数据处理返回
}