开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:管道阻塞的机制】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9758
管道阻塞的机制
内容介绍:
一、goroutine 和 channel 结合
应用实例1
应用实例2-阻塞
一、goroutine 和 channel结合
应用实例1
请完成 goroutine 和 channel 协同工作的案例,
具体要求:
1)开启一个 writeData 协程,向管道 intChan 中写入50个整数.
2)开启一个 readData 协程,从管道 intChan 中读取 writeData 写入的数据。
3)注意: writeData 和 readDate 操作的是同一个管道
4)主线程需要等待 writeData 和 readDate 协程都完成工作才能退出【管道】
思路分析示意图如下:
遇到管道多的情况,要利用画思路图来帮助我们理解和分析,进而解决问题。
代码的实现:
1package main
2import (
3 "fmt"
4 )
5
6
7 //write Data
8 func writeData(intchan chan int) {
9 for i := 1; i <= 50; i++ {
10 //放入数据
11 intchan<- i
12 }
13 }
14
15
16//read data
17 func readData(intchan chan int,exitchan chan bool){
18
19for {
20 v, ok := <-intchan
21 if !ok {
22break
23 }
24 fmt.Printf( "readData读到数据=%v\n", v)
25 }
26 // readData读取完数据后,即任务完成
27 exitchan<- true
28 close(exitchan)}
29
30 //创建两个管道
31 intchan := make( chan int. 50)
32 exitchan := make(chan bool,1)
33 go writeData(intchan)
34 go readData(intchan, exitchan)
35 //time-sleep(time.second*10)
36 for{
37 ok := <-exitchan
38if !ok {
39break
40}
41}
应用实例2-阻塞
func main() {
intchan := make(chan int, 10) l/10->50的话数据一下就放入了
exitChan := make(chan bool,1)
go writeData(intChan)
//go readData(intChan, exitChan)
//就是为了等待..readData 协程完成
for _ = range exitChan {
fmt.Println("ok...")
}
问题:如果注销掉 go readData(intChan,exitChan),程序会怎么样?
分析:注销后,只有写入数据,没有读取数据,如果编译器(运行)发现一个管道只有写入,而没有读取,写入的数据超过其容量,则该管道会阻塞。但如果两个协程同时存在时,写管道和读管道的频率不一致,无所谓。
只有写入的过程,没有读取的过程,写的依次增加,最终会使管道因数据增多而阻塞,管道很小,数据量很大,写入的数据超过超过管道容量,会发生报错。
写的快,读的慢时,不会失锁,因为编译器会进行分析,不会发生阻塞。即管道不停的流动就不会报错和发生错误。
写的速度很快,读取时一秒钟读取一次,显然速度是不同步的,但仍然不会报错。编译器的编译结果如下:(没有发生报错)
答: 如果只是向管道写入数据,而没有读取,就会出现阻塞而 dead lock,原因是intChan 容量是10,而代码 writeData 会写入50个数据,因此会阻塞在 writeData 的 ch <- i
如果编译器(运行),发现一个管道只有写入的过程,而没有读取的过程,则该管道会发生阻塞。
假设开设50个管道,写入50个数据,但有一个地方没有被读取,就不会发生阻塞。如果发生阻塞,可能是其他原因导致的。
写管道和读管道的频率不一致,不会发生阻塞,是有意义的阻塞,在时间足够的情况下,能够完成相应工作。当发现没有等下去的希望时,就会发生报错。
运行时,编译器底层会有一种分析机制,不断维护着上下文。
注:
注销掉 go readData(intChan,exitChan) 后,编译器的编译结果如下:(发生阻塞deadlock)