注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

snoopyxdy的博客

https://github.com/DoubleSpout

 
 
 

日志

 
 

golang并发示例(一)  

2013-09-03 17:01:43|  分类: golang |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
1、素数1
素数筛选是一个比较经典的问题(这里侧重于Eratosthenes素数筛选算法的并行特征)。它以全部的 自然后为筛选对象。首选从第一个素数2开始,后续数列中是已经素数倍数的数去掉。每次筛选可以得到 一个新的素数,然后将新的素数加入筛选器,继续筛选后面的自然数列(这里要参考算法的描述调整)。

为了产生整数序列,我们使用管道。管道可以用于连接两个并行的处理单。在Go语言中, 管道由运行时库管理,可以用"make"来创建新的管道。

我们新建找出100以内的素数

func generate(ch chan int) { for i := 2; i<100; i++ { ch <- i // Send 'i' to channel 'ch'. } }

函数"generate"用于生成2, 3, 4, 5, ...自然数序列,然后依次发送到管道。 这里用到了二元操作符"<-", 它用于向管道发送数据。当管道没有接受者的时候 会阻塞,直到有接收者从管道接受数据为止,所以不会出现疯狂的生产而来不及消费的问题。

过滤器函数有三个参数:输入输出管道和用于过滤的素数。当输入管道读出来的数不能被 过滤素数整除时,则将当前整数发送到输出管道。这里用到了"<-"操作符,它用于从 管道读取数据。

// Copy the values from channel 'in' to channel 'out', // removing those divisible by 'prime'. func filter(in, out chan int, prime int) { for { i := <-in // Receive value of new variable 'i' from 'in'. if i % prime != 0 { out <- i // Send 'i' to channel 'out'. } } }

整数生成器generator函数和过滤器filters是并行执行的。Go语言有自己的并发 程序设计模型,这个和传统的进程/线程/轻量线程类似。为了区别,我们把Go语言 中的并行程序称为goroutines。如果一个函数要以goroutines方式并行执行, 只要用"go"关键字作为函数调用的前缀即可。goroutines和它的启动线程并行执行, 但是共享一个地址空间。

func main() { ch := make(chan int) // Create a new channel. go generate(ch) // Start generate() as a goroutine. for { prime := <-ch fmt.Println(prime) ch1 := make(chan int) go filter(ch, ch1, prime) ch = ch1 } }

29行先调用"generate"函数,用于产生最原始的自然数序列(从2开始)。然后 从输出管道读取的第一个数为新的素数,并以这个新的素数生成一个新的过滤器。 然后将新创建的过滤器添加到前一个过滤器后面,新过滤器的输出作为新的输出 管道。

程序说明:
程序说明,虽然会报错,但是不影响功能
整个程序可以理解为:


generate -> filter1(in,out,2) -> filter2(in,out,3) -> filter3(in,out,5) -> ...
generate负责生产素数,第一次生成2阻塞掉循环
filter1收到in:generate的ch,收到out:传入给prime变量的ch,以及素数实参2
filter1开始执行前,main中的变量prime开始接受filter1的输出
开始执行filter1,in ch收到generate函数传递过来的3,发现不能被2整除,于是 out <- 3

这时main函数的prime收到3,打印3,同时新建一个out ch再启动一个go的线程filter2
在执行filter2之前prime等待filter2的out输出

同时,filter1,filter2,main以及generate是同时执行的
filter2开始执行,阻塞循环,传入的in ch 表示filter1的out,out ch这时是main的prime在等待消费,执行等待filter的out过来的in ch ...
因为filter1消费了一次in,所以generate又生产了4,这时filter1因为不满足 4%2 != 0 这个条件,继续循环,消费generate
filter1继续消费到5,因为 5%2 != 0 成立,这是filter 执行out ch,生产数据5到filter2
filter2收到5,执行 5%3, 5%3 !=0 成立,所以 filter2 输出out到main的prime,打印素数5

接下来就继续执行了



2、素数2
sieve程序还可以写的更简洁一点。这里是"generate"的改进。

func generate() chan int { ch := make(chan int) go func(){ for i := 2; ; i++ { ch <- i } }() return ch }

新完善的generate函数在内部进行必须的初始化操作。它创建输出管道,然后 启动goroutine用于产生整数序列,最后返回输出管道。它类似于一个并发程序 的工厂函数,完成后返回一个用于链接的管道。
第12-16行用go关键字启动一个匿名函数。需要注意的是,generate函数的"ch" 变量对于匿名函数是可见,并且"ch"变量在generate函数返回后依然存在(因为 匿名的goroutine还在运行)。

这里我们采用过滤器"filter"来筛选后面的素数:

func filter(in chan int, prime int) chan int { out := make(chan int) go func() { for { if i := <-in; i % prime != 0 { out <- i } } }() return out }

函数"sieve"对应处理的一个主循环,它只是依次将数列交给后面的素数筛选器进行筛选。 如果遇到新的素数,再输出素数后以该素数创建信的筛选器。

func sieve() chan int { out := make(chan int) go func() { ch := generate() for { prime := <-ch out <- prime ch = filter(ch, prime) } }() return out }

主函数入口启动素数生成服务器,然后打印从管道输出的素数:

func main() { primes := sieve() for { fmt.Println(<-primes) } }

代码简要说明:
main函数入口,执行sieve函数,返回out ch
这样代码很清晰,main函数只是执行循环,等待primes的生产,然后消费不停的循环
sieve开始执行,定义 out ch,在sieve里执行一个线程闭包,获得generate的ch
执行循环,prime等待generate的生产
下面同prime前一个例子相同,只是filter的out步是filter里生成的,ch的赋值不再通过直接赋值ch1来实现,而是通过函数的返回值来实现


  评论这张
 
阅读(2211)| 评论(1)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016