Golang学习日记(八)——goroutine与channel
Goroutine与Channel
协程(Co-routine)与线程(Thread)
内存空间被划分为内核空间与用户空间,其中内核空间仅对CPU可见。
Go语言调度器(GMP):
G——>goroutine协程
M——>processor处理器
P——>thread线程
调度器特点:
复用线程:
- work stealing机制:若P的本地队列为空,可获取其他队列不为空的其他P队列中的协程。
- hand off机制:若当前P阻塞,则创建或唤醒新P,让新P接管被阻塞的队列。
利用并行:可设置GOMAXPROCS限定P的个数
- 抢占:可剥夺当前执行进程。
- 全局G队列:在执行work stealing策略时,该队列在解锁或加锁后,可供P使用。
Goroutine的创建与退出
go
1 | func newTask(){ |
注意,goroutine的生命周期受到父线程控制,如上述例子中,若main线程结束,则newTask也将被结束。
执行runtime.Goexit()可结束当前goroutine。
go
1 | func main() { |
匿名函数的调用
go
1 | func main(){ |
管道(Channel)的定义与使用
channel内部实现goroutine间的同步。
go
1 | func main() { |
channel分为有缓存channel与无缓冲channel,
有缓存channel:输入方可随时退出channel,直到channel为满;接受方可随时取出数据,直到channel为空。
go1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20func main() {
//创建一个缓存区为3的channel
c:=make(chan int,3)
go func(){
defer fmt.Println("goroutine end")
for i:=0;i<4;i++{
c<-i
//使用len(c),cap(c)可以查看当前channel的长度与容量。
fmt.Println(i,"len(c)=",len(c),"cap(c)=",cap(c))
}
}()
time.Sleep(2*time.Second)
//依次从channel中读出数据
for i:=0;i<4;i++{
num:=<-c
fmt.Println(num)
}
fmt.Println("main end")
}无缓冲channel:输入方将数据传入channel后,直到接收方进入channel取出数据,才能退出channel。
管道的关闭
使用close(channel)可以关闭该通道。
go
1 | func main() { |
注意:
- 关闭channel后,若再向channel发送数据,则会导致panic错误并返回零值。
- 关闭channel后,仍可以从channel中读出数据。
- 对于nil channel(未分配空间的channel),无论收发都会被阻塞。
关键字Range与Select
对于如下代码段,可以使用 range 对代码进行优化:
go
1 | for { |
简化代码如下:
go
1 | //迭代地从channel中读出数据 |
使用 Select 能够监控多个channel的状态:
go
1 | //使用select监听多个channel |