在前面的章节中介绍了
打印1-100000之间的素数
的三种方法中的前两种;尽管使用了goroutine,开了四个协程之后,运算时间在一定程度上压缩了不少。但是,单单使用goroutine无法实现各个数据之间的共享,今天尝试在此基础上寻找更优解:
- 传统方法,通过for循环遍历各个数
- 使用并发或并行,将统计素数的任务分配给多个goroutine去完成
- goroutine结合channel
goroutine结合channel
var wg sync.WaitGroup
func putNum(intChan chan int) {
for i := 2; i < 100000; i++ {
intChan <- i
}
close(intChan)
wg.Done()
}
//判断intChan是否为素数,如果是素数,放到primeNum中
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
for num := range intChan {
var flag = true
for i := 2; i < num; i++ {
if num%i == 0 {
flag = false
break
}
}
if flag {
//fmt.Println(num, "是素数")
primeChan <- num
}
}
//报错send on closed channel如果一个chanel关闭,就无法再发送数据了
//close(primeChan)
exitChan <- true
wg.Done()
}
func printPrime(primeChan chan int) {
//for v := range primeChan {
//fmt.Println(v)
//}
wg.Done()
}
func main() {
//协程数
channelNum := 10
start := time.Now().UnixNano()
//存放数字
intChan := make(chan int, 1000)
//存放素数
primeChan := make(chan int, 10000)
//标识primeChan是否关闭
exitChan := make(chan bool, channelNum)
//存放数字的协程
wg.Add(1)
go putNum(intChan)
for i := 0; i < channelNum; i++ {
//统计素数的协程
wg.Add(1)
go primeNum(intChan, primeChan, exitChan)
}
//打印素数的协程
wg.Add(1)
go printPrime(primeChan)
//判断exitChan是否存满值,如果存慢值,则表示协程执行结束
wg.Add(1)
go func() {
for i := 0; i < channelNum; i++ {
<-exitChan
}
//关闭primeChan
close(primeChan)
wg.Done()
}()
wg.Wait()
end := time.Now().UnixNano()
fmt.Println("end...", (end-start)/1e6)
//end... 622
}
保持计算量不变的情况下,开启10个协程之后,计算时间压缩到了622ms
select多路复用
在某些场景下,需要同时从多个通道接收数据,这时就用到了select多路复用
select {
case <-ch1:
...
case data := <-ch2
...
case ch3<-data:
...
default:
默认操作
}
- 使用select来获取channel时,不需要关闭channel
func main() {
//定义一个管道 10个数据int
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
//定义一个管道 5个string数据
stringChan := make(chan string, 5)
for i := 0; i < 5; i++ {
stringChan <- "hello" + fmt.Sprintf("%d", i)
}
close(intChan)
for {
select {
case v := <-intChan:
fmt.Println("从intChan读取的数据", v)
time.Sleep(time.Millisecond * 50)
case v := <-stringChan:
fmt.Println("从stringChan读取的数据",v)
default:
fmt.Printf("数据获取完毕")
return
}
}
}
//从stringChan读取的数据 hello0
//从stringChan读取的数据 hello1
//从intChan读取的数据 0
//从stringChan读取的数据 hello2
//从stringChan读取的数据 hello3
//从stringChan读取的数据 hello4
//从intChan读取的数据 1
//从intChan读取的数据 2
//从intChan读取的数据 3
//从intChan读取的数据 4
//从intChan读取的数据 5
//从intChan读取的数据 6
//从intChan读取的数据 7
//从intChan读取的数据 8
//从intChan读取的数据 9
//数据获取完毕