管道阻塞的机制
- 案例:
intChan := make(chan int, 10) exitChan := make(chan bool, 1) go writeData(intChan) go readData(intChan, exitChan) for { _, ok := <-exitChan if !ok { break } }
- 问题:如果注销掉 go readData(intChan, exitChan),程序会怎么样?
- 答:如果只是向管道写入数据,而没有读取,就会出现阻塞而dead lock,原因是intChan容量是10,而代码writeData会写入50个数据,因此会阻塞在writeData的ch<-i
- 如果,编译器(运行),发现一个管道只有写,而没有读,则该管道,会阻塞。
- 写管道和读管道的频率不一致,无所谓。
协程求素数
- 需求:
- 要求统计 1-80000 的数字中,哪些是素数?
- 分析思路:
- 传统的方法,就是使用一个循环,循环的判断各个数是不是素数。
- 使用并发/并行的方式,将统计素数的任务分配给多个(4 个)goroutine 去完成,完成任务时间短。
- 分析示意图:
- 代码实现:
package main import ( "fmt" ) //向 intChan放入 1-8000个数 func putNum(intChan chan int) { for i := 1; i <= 80000; i++ { intChan<- i } //关闭intChan close(intChan) } // 从 intChan取出数据,并判断是否为素数,如果是,就/放入到primeChan func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) { var flag bool // for { num, ok := <-intChan //intChan 取不到.. if !ok { break } flag = true //假设是素数 //判断num是不是素数 for i := 2; i < num; i++ { if num % i == 0 {//说明该num不是素数 flag = false break } } if flag { //将这个数就放入到primeChan primeChan<- num } } fmt.Println("有一个primeNum 协程因为取不到数据,退出") exitChan<- true } func main() { intChan := make(chan int , 1000) primeChan := make(chan int, 20000)//放入结果 //标识退出的管道 exitChan := make(chan bool, 4) //开启一个协程,向 intChan放入 1-8000个数 go putNum(intChan) //开启4个协程,从 intChan取出数据,并判断是否为素数,如果是,就放入到primeChan for i := 0; i < 4; i++ { go primeNum(intChan, primeChan, exitChan) } go func(){ for i := 0; i < 4; i++ { <-exitChan } //当我们从exitChan 取出了4个结果,就可以放心的关闭 primeChan close(primeChan) }() //这里主线程,进行处理 //遍历 primeChan ,把结果取出 for { res, ok := <-primeChan if !ok{ break } //将结果输出 fmt.Printf("素数=%d\n", res) } fmt.Println("main线程退出") }
- 截取部分运行结果:
测试协程求素数的效率
- 使用协程效率测试:
func main() { intChan := make(chan int , 1000) primeChan := make(chan int, 20000)//放入结果 //标识退出的管道 exitChan := make(chan bool, 4) // 4个 start := time.Now().Unix() //开启一个协程,向 intChan放入 1-8000个数 go putNum(intChan) //开启4个协程,从 intChan取出数据,并判断是否为素数,如果是,就放入到primeChan for i := 0; i < 4; i++ { go primeNum(intChan, primeChan, exitChan) } //这里我们主线程,进行处理 go func(){ for i := 0; i < 4; i++ { <-exitChan } end := time.Now().Unix() fmt.Println("使用协程耗时=", end - start) //当我们从exitChan 取出了4个结果,就可以放心的关闭 primeChan close(primeChan) }() //遍历我们的 primeChan ,把结果取出 for { _, ok := <-primeChan if !ok{ break } //将结果输出 //fmt.Printf("素数=%d\n", res) } fmt.Println("main线程退出") }
- 输出结果:
- 传统方法效率测试:
package main import ( "time" "fmt" ) func main() { start := time.Now().Unix() for num := 1; num <= 80000; num++ { flag := true //假设是素数 //判断num是不是素数 for i := 2; i < num; i++ { if num % i == 0 {//说明该num不是素数 flag = false break } } if flag { //将这个数就放入到primeChan //primeChan<- num } } end := time.Now().Unix() fmt.Println("普通的方法耗时=", end - start) }
- 输出结果;