需求:
使用多个goroutine和channel 统计1-80000的数字中,哪些是素数? [测试数据:80000]
分析思路:
传统的方法,就是使用一个循环,循环的判断各个数是不是素数【ok】。
使用并发/并行的方式,将统计素数的任务分配给多个(4个)goroutine去完成,完成任务时间短。
代码:
package main
import (
"fmt"
"time"
)
// 向 intChan放入 1-80000个数
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) {
//使用for 循环
// var num int
var flag bool //
for {
//time.Sleep(time.Millisecond * 10)
num, ok := <-intChan
if !ok {
// 如果 numChan 已经关闭,则退出循环
break
}
flag = true //假设是素数
//判断num是不是素数,如果不是,标记为false
for i := 2; i < num; i++ {
if num%i == 0 { //说明该num不是素数
flag = false
break
}
}
if flag {
// 如果 num 是素数,则将 num 写入 resChan 中
primeChan <- num
}
}
fmt.Println("有一个primeNum 协程因为取不到数据,退出")
//这里我们还不能关闭 primeChan
//向 exitChan 写入true,表示 goroutine 执行完毕
exitChan <- true
}
func main() {
intChan := make(chan int, 1000)
primeChan := make(chan int, 20000) //放入结果
exitChan := make(chan bool, 4) // 标识退出的管道 4个值
start := time.Now().Unix()
//开启一个协程,向 intChan放入数据
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 // 取出值,但不用判别,如果取不出四个就在这一步等待
}
//也可以判定len(exitChan)是否为4,是4就可以关闭
end := time.Now().Unix()
fmt.Println("使用协程耗时=", end-start)
//当我们从exitChan 取出了4个结果,就可以放心的关闭 prprimeChan
close(primeChan)
}()
//遍历我们的 primeChan ,把结果取出
for {
_, ok := <-primeChan //res, ok := <-primeChan
if !ok {
break
}
// 输出所有的素数
//for res := range resChan {
// fmt.Printf("%d is a prime number\n", res)
//}
}
fmt.Println("main线程退出")
}
多协程效率会有所提升,但不完全成线性,因为CPU的核数是有限的,如4核CPU最多实现4个goroutinue的并行,当性能达到极限时,即使设置再多的goroutine也无法提升效率。