go 并发编程实战 : 统计 1 - 30 万之间所有素数

示例代码

package main

import (
	"sync"
	"time"
)

var wg sync.WaitGroup

// 向数字管道内存放数据
func putNum(intChan chan int) {
	for i := 2; i < 300000; i++ {
		intChan <- i
	}
	close(intChan)
	wg.Done()
}

// 从数字管道中取值判断是否素数并保存到素数管道
func primeNum(intChan chan int, primeChan chan int, doneChan chan int) {
	for num := range intChan {
		res := true
		for i := 2; i < num; i++ {
			if num%i == 0 {
				res = false
				break
			}
		}
		if res {
			primeChan <- num
		}
	}
	wg.Done()
	// 记录协程执行完毕
	doneChan <- 1
}

// 打印数据
func printNum(primeChan chan int) {
	// for data := range primeChan {
	// 	println(data)
	// }
	wg.Done()
}

func main() {
	start := time.Now().Unix()
	// 定义存放 1 - 12万的数字的管道
	intChan := make(chan int, 1000)
	// 存放素数的管道
	primeChan := make(chan int, 50000)

	// 存放数据
	wg.Add(1)
	go putNum(intChan)

	// 从数字管道中取值判断是否素数并保存到素数管道
	// 开启多个协程
	// 此处开启了多个协程向管道 primeChan 写入数据
	// 而 打印数据 函数需要关闭管道, 那么如何关闭管道呢?
	// 当 16个协程执行完毕时关闭管道
	// 利用一个管道记录协程是否执行完毕
	doneChan := make(chan int, 16)
	for i := 0; i < 16; i++ {
		wg.Add(1)
		go primeNum(intChan, primeChan, doneChan)
	}

	// 打印数据
	wg.Add(1)
	go printNum(primeChan)

	// 判断16次协程是否执行完毕
	go func() {
		for i := 0; i < 16; i++ {
			<-doneChan
		}
		// 关闭素数统计管道
		close(primeChan)
		close(doneChan)
	}()
	wg.Wait()
	// 程序结束
	println("运行时间 :", time.Now().Unix()-start)
	println("素数个数 :", len(primeChan))

}

运行结果

运行时间 : 2 秒
素数个数 : 25997

比没有开启协程时运行效率明显提高 ( 同一台机器非协程模式需要 13 秒 )。