本文通过一个例子来展示Go语言的魅力。
打印输出前n个数中的质数
在开始学习编程时,很多人都会遇到这个经典的题目,打印前n个数中的质数。但如何并行解决这个问题呢?
一个直接的思路是,如果我们打印前100个数中的质数,我们直接找100个工人,每个工人计算其中一个数是不是质数,如果是质数则输出该数。虽然这是一个可行的解决方案,但不够优雅。特别是较小数容易判断是否是质数,较大数不容易判断,造成了工人之间负载不平衡。
下面有一段很好的代码展示了如何并行解决这个问题。代码用Go语言实现,这个例子也展示了Go语言的魅力。
这个解决方案的核心思想在于,将判断是否是质数转换为一个流水线任务。假如我们有100个工人,编号从1到100.当需要判断数字29是不是质数时,只需要向1号工人问一下,29能不能被2整除,向2号工人问一下,29能不能被3整除…这样如果前28个工人都回答不能整除,则29就是质数。
上面的计算仍有冗余,可以优化。判断一个数是否是质数,只需要拿比它小的质数除一下,并不需要比它小的所有数。这样我们需要计算2,3,5…的工人,而不需要计算4的工人。
最后一点,工人是个动态增多的过程,当我们判断出一个质数时,才需要新增这个工人。例如,当我们需要判断2,3,4,5,6…是否是质数,我们知道2是质数,则产生一个工人负责计算2,当3来到时,发现3不能被2整除,则产生一个工人负责计算3,以此类推。
下面我们看源码。
1 | func primetask(c chan int) { |
在主函数进行调用时,用以下方式
1 | c := make(chan int) |
每调用一次primetask时,产生一个工人。第一次在主函数调用go primetask(c)时,这个c就是流水线的输入。先输入2,这时函数内p的值变为2,然后打印输出2,表明2是一个质数。从此以后这个函数负责计算新来的值是不是被2整除。另外这个函数还需要构造和下一个工人的通道,即nc,当这个工人发现新来的数字不能被2整除时,需要通过nc交给下一个工人。每个工人第一次接受到的值(p)是不能被前面所有工人整除的值,即是一个质数,打印输出。之后,每个工人需要检查新来的值是否能被p整除,如果能,则无需发给下一个工人,如果不能,则发给下一个工人。
通过这个小例子可以看到Go语言精妙的地方。一门优秀的语言往往能解放思想,提高生产力。