协程
在java/c++中我们要实现并发编程的时候,我们通常需要自己维护一个线程池,并且需要自己去包装一个又一个的任务,同时需要自己去调度线程执行任务并维护上下文切换,这一切通常会耗费程序员大量的心智。那么能不能有一种机制,程序员只需要定义很多个任务,让系统去帮助我们把这些任务分配到CPU上实现并发执行呢?
Go语言中的goroutine就是这样一种机制,goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。
在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能–goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了,就是这么简单粗暴。
1、概念
线程属于内核态,比较消耗资源,栈MB级别
协程:用户态,轻量级线程,栈KB级别
2、语法
go func(传入参数A 传入参数A类型,传入参数B 传入参数B类型,……){
函数体
}(传入参数A 传入参数A类型,传入参数B 传入参数B类型,……)
3、协程之间的通信
(1) 基本逻辑关系
通过通信共享内容,而不是通过共享内存来实现通信
(2) Channel类型
make(chan 元素类型, [ 缓冲大小 ] )
-
无缓冲通道
使用无缓冲通道发送数据时,需要保证发送方和接收方都同时准备好,这样才能完成通信。
如果两个goroutine没有同时准备好,通道会导致先执行发送或接收操作的goroutine阻塞等待。
-
有缓冲通道
有缓冲通道,指通道可以保存多个值.
如果给定了一个缓冲区容量,那么通道就是异步的,只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行
(3)理解
Channel(管道)可以被认为是协程之间通信的管道。
与水流从管道的一端流向另一端一样,数据可以从信道的一端发送并在另一端接收。
(4)示例代码
-
生产者:A协程
生产者只需要顾及生产,所以使用不带缓冲区的Channel来模拟
-
消费者:B协程
消费者需要进行一些复杂操作,所以使用带缓冲区的Channel来模拟
package main
import "fmt"
func CalSquare() {
src := make(chan int) // 定义无缓冲队列
dest := make(chan int, 3) // 有缓冲队列
// A协程的功能
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
// B协程的功能
go func() {
defer close(dest)
for i := range src {
dest <- i + i
}
}()
// 打印输出
for i := range dest {
// 可能存在的复杂操作
fmt.Println(i/2, "+", i/2, "=", i)
}
}
func main() {
fmt.Println("Channel")
CalSquare()
}
defer语句的用法:
【1】defer语句会将函数推入到一个列表中。
【2】同时列表中的函数会在return语句执行后被调用。
【3】defer常常会被用来简化资源清理释放之类的操作。