素数筛【并发特性】(个人理解)
// 返回生成自然数序列的管道: 2, 3, 4, ...
func GenerateNatural() chan int {
ch := make(chan int)
go func() {
for i := 2; ; i++ {
ch <- i
}
}()
return ch
}
// 管道过滤器: 删除能被素数整除的数
func PrimeFilter(in <-chan int, prime int) chan int {
out := make(chan int)
go func() {
for {
if i := <-in; i%prime != 0 {
out <- i
}
}
}()
return out
}
func main() {
ch := GenerateNatural() // 自然数序列: 2, 3, 4, ...
for i := 0; i < 10; i++ {
prime := <-ch // 新出现的素数
fmt.Printf("%v: %v\n", i+1, prime)
ch = PrimeFilter(ch, prime) // 基于新素数构造的过滤器
}
}
GenerateNatural()函数,用于生成自然数序列,并返回一个自动获取自然数的管道ch
PrimeFilter()函数,每次生成新的管道out,用于放置非本素数的倍数的自然数
main函数解释
1. main函数开启,执行在goroutine1
2. 调用GenerateNatural()函数,生成goroutine2,用于生成生成自然数序列,并把自然数放到为ch的管道用于传输
3. 第一次ch管道传输过来的是2,为素数,直接输出
4. 调用PrimeFilter()函数,生成goroutine3一直监听ch管道的数据,生成out1管道,用于放置大于2的非2的倍数的自然数
5. 返回out1管道,用于获取大于2素数,并把out1设置成==当前管道==
6. 第4步的goroutine3,一直在读取ch管道的数据,读到数字3,判断不为2的倍数,把数字3放置到out1管道
7. 当前管道获取到数据,循环继续
8. 重复第4步,生成goroutine4,监听的是out1的管道的数据(即将不是2的倍数数据接手过来,判断是否为3的倍数),生成out2管道,用于放置大于3的非3的倍数的自然数
9. 返回out2管道,用于获取大于3的素数,并把out2设置成当前管道
10. 第4步的goroutine3,一直在读取ch管道的数据,读到数字4,判断为2的倍数,直接跳过
11. 继续读取到5,判断不为2的倍数,将5放置到out1管道,
12. 第8步的goroutine4,一直在读取out1管道的数据,读取到数字5,判断不为3的倍数,放置到out2
13. 当前管道获取到数据,循环继续
14. 重复第4步,生成goroutine5,监听的是out2的管道的数据(即将不是3的倍数数据接手过来,判断是否为5的倍数),生成out3管道,用于放置大于5的非5的倍数的自然数
15. ......循环
所以整个素数筛就是从goroutine2(筛选非2倍数)=>goroutine4(筛选非3倍数)=>goroutine5(筛选非5倍数),组成了一条链表式的素数筛,整个流程如下图
素数筛图示