go version go1.11 windows/amd64

本文为解读 参考链接1 中的 菊花链 一节 的示例程序,此程序和 参考链接2 中代码有些类似:前者有范围,后者是无限循环。清楚了 参考链接1 的逻辑,就能理解 参考链接2 的代码。

测试代码——测试语句使用蓝色字:

package main 

import (
"fmt"
) // 6.菊花链
// 数据从一端流入,从另一端流出,看上去好像一个链表
// 过滤器
func xrange2() chan int {
// 从2开始自增的整数生成器
var ch chan int = make(chan int)
fmt.Println("xrange2 - ch @ ", ch)
go func() {
// 开出一个goroutine
for i := 2; ; i++ {
// 直到信道索要数据,才把i添加进信道
fmt.Println("xrange2: i = ", i)
ch <- i
}
} () return ch
} func filter(in chan int, number int) chan int {
// 输出一个整数队列,筛出是number倍数的,不是number的倍数的放入输出队列
// in: 输入队列
out := make(chan int)
fmt.Println("\nfilter - in @ ", in)
fmt.Println("filter - number: ", number)
go func() {
for {
i := <- in // 从输入中取一个
fmt.Println("filter - in @ ", in, ", i = ", i) if i % number != 0 {
fmt.Println("放入输出信道 in @ ", in, ", number = ", number)
out <- i // 放入输出信道
}
}
} () return out
} func main() {
// 6.菊花链
const max = // 找出10以内的所有素数 // 修改为10,便于通过测试语句理解逻辑
nums := xrange2() // 初始化一个整数生成器
number := <- nums // 从生成器中抓一个整数(2),作为初始化整数
fmt.Println("0.main - nums @ ", nums)
// number作为筛子,当筛子超过max的时候结束筛选
for number <= max {
fmt.Println(number) // 打印素数,筛子是一个素数 // 源代码,
nums = filter(nums, number) // 筛掉number的倍数
fmt.Println("1.main - nums @ ", nums)
number = <- nums
}
}

测试结果:

xrange2 - ch @  0xc000050060
xrange2: i = 2
xrange2: i = 3
0.main - nums @ 0xc000050060
2 filter - in @ 0xc000050060
filter - number: 2
1.main - nums @ 0xc0000500c0
filter - in @ 0xc000050060 , i = 3
放入输出信道 in @ 0xc000050060 , number = 2
3 filter - in @ 0xc0000500c0
filter - number: 3
1.main - nums @ 0xc000050120
xrange2: i = 4
xrange2: i = 5
filter - in @ 0xc000050060 , i = 4
filter - in @ 0xc000050060 , i = 5
放入输出信道 in @ 0xc000050060 , number = 2
filter - in @ 0xc0000500c0 , i = 5
放入输出信道 in @ 0xc0000500c0 , number = 3
5 filter - in @ 0xc000050120
filter - number: 5
xrange2: i = 6
xrange2: i = 7
filter - in @ 0xc000050060 , i = 6
filter - in @ 0xc000050060 , i = 7
放入输出信道 in @ 0xc000050060 , number = 2
filter - in @ 0xc0000500c0 , i = 7
放入输出信道 in @ 0xc0000500c0 , number = 3
filter - in @ 0xc000050120 , i = 7
放入输出信道 in @ 0xc000050120 , number = 5
1.main - nums @ 0xc000050180
7 filter - in @ 0xc000050180
filter - number: 7
1.main - nums @ 0xc00001c060
xrange2: i = 8
xrange2: i = 9
filter - in @ 0xc000050060 , i = 8
filter - in @ 0xc000050060 , i = 9
放入输出信道 in @ 0xc000050060 , number = 2
filter - in @ 0xc0000500c0 , i = 9
xrange2: i = 10
xrange2: i = 11
filter - in @ 0xc000050060 , i = 10
filter - in @ 0xc000050060 , i = 11
放入输出信道 in @ 0xc000050060 , number = 2
filter - in @ 0xc0000500c0 , i = 11
放入输出信道 in @ 0xc0000500c0 , number = 3
filter - in @ 0xc000050120 , i = 11
放入输出信道 in @ 0xc000050120 , number = 5
filter - in @ 0xc000050180 , i = 11
放入输出信道 in @ 0xc000050180 , number = 7

解读

参考链接1 中存在一个Daisy-chain的示意图。

说明,第一次看这个程序时,完全没看懂,这几天看了更多资料后,再添加了写调试语句,运行多次才理解了这个程序——下午痛下决心搞明白它,也因此有了本文。

在 xrange2() 函数中,建立了一个 信道XD1,用它来发送 大于等于2 的整数(生成器)。因为是非缓冲型信道,所以,在其发送后会被阻塞,直到发送的数据被接收,接收后继续下一个数发送。

在 xrange2() 函数中创建了一个goroutine(协程XC1),用来实现 让 信道XD1 发送数据,一直在运行,直到主程序(线程)结束。

在 main() 函数中,首先调用 xrange2() 函数建立一个 整数生成器nums(注意它的地址),再把初始的素数2赋值给number——来自信道XD1 发送的第一个数——接收完毕后,信道XD1又继续发送下一个整数3。

接着进入循环——有限,关键来了!调用 filter()函数 并将其返回值赋值给nums——这里,nums就改变了——测试语句中打印的信道的地址改变了!

说明,孤在理解这里的时候花了不少时间。

那么,filter()函数 中做了什么呢?新建了一个信道out,并把这个信道返回;另外,创建了一个goroutine——包含一个无限循环,从参数 信道in 中 获取一个值,然后将这个值和传入的参数(素数)number进行运算比较,如果获取的数 不能被 number 除尽,那么,使用信道out发送。无限循环 意味着这个goroutine会一直运行——直到主程序退出。

第一次调用时,从输入参数信道in中获取的数是 3,这个信道就是xrange2()函数中建立的信道XD1——这里收到了3 那么XD1发送4 然后等待下一次接收。3除以2除不尽,此时,信道out发送3,然后,等待下一次信道XD1发送数据。因为是无限循环,下一次的数据是4,在filter()函数中建立的第一个goroutine中收到了——信道XD1又发送5(阻塞),但它是2的倍数,因此被忽略。再次循环,受到5,无法被2除尽,使用out发送。

上面已经有两个goroutine了,现在回到主线程main函数中。

信道nums 成为了 filter()函数 中的out,获取了 第一个被2除不尽的 3,3小于max 10,开始 下一轮循环——打印3、再次调用 filter()函数。

filter()函数 新建信道out——地址变了、创建新的一个goroutine!这个新的goroutine里面的number是 3——筛掉3的倍数,之前的一个是2——筛掉2的倍数。

新的goroutine里面的的输入参数信道in为之前一个goroutine的信道out。在前面,第一个goroutine已经发送到了5,但没有被接收,因此,阻塞了。

在第二个goroutine的循环中,首先就是接收,接收到前面发送的5——filter()函数 创建的第一个goroutine又继续运行了 直到发送7。5不能被3除尽,第二个goroutine发送5。

和前面一样,第二个goroutine的out被赋值给了 主程序main()函数 中的nums——再次改变了nums!在主程序中执行时,nums收到了5——也就是第三个素数。

然后,主程序继续循环,将filter()函数中新建的信道out当作参数传递给下一次filter()函数调用——作为其参数信道in,继续运行下去,会出现很多的goroutine。这些goroutine通过信道相连,因此,这种方式就叫做 Daisy-chain(菊花链)。

需要注意,每个 goroutine 都会一直运行——直到主程序退出(参考链接2 中是不会退出的)。

若是要查找的素数的范围较大,那么,会存在成千上万个goroutine。虽然goroutine消耗的资源(测试将max设置为10亿,CPU利用率一直是100%) 极少,但是,这种方式也是存在缺陷的。

起点是 xrange2() 函数中的 信道XD1,它作为调用 filter()函数 新建的goroutine 的输入信道,源源不断地提供整数;

filter() 函数 中的每一个新建信道out 都作为 下一次调用 filter()函数 的输入信道;

filter() 函数 的每次调用都有参数number,此参数为素数,每次基于number建立一个goroutine,删除是number倍数的整数;

画个图看看:

参考链接

1.Go语言并发的设计模式和应用场景

2.官文An example package

解读使用Daisy-chain(菊花链)方式筛选一定范围内素数的代码的更多相关文章

  1. O(n)线性筛选n以内的素数

    O(n)线性筛选n以内的素数 (1)对于任何一个素数p,都不可能表示为两个数的乘积 (2)对于任何一个合数m = p1a1p2a2…pmam,这里p1< p2 < … <pm,都能使 ...

  2. LightOj 1197 - Help Hanzo(分段筛选法 求区间素数个数)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1197 题意:给你两个数 a b,求区间 [a, b]内素数的个数, a and b ( ...

  3. 大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2)

              大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out  of  bag  data及代码(2) 上一节中我们讲解了随机森林的基本概念,本节的话我们讲解随机森 ...

  4. 史上最全的CSS hack方式一览 jQuery 图片轮播的代码分离 JQuery中的动画 C#中Trim()、TrimStart()、TrimEnd()的用法 marquee 标签的使用详情 js鼠标事件 js添加遮罩层 页面上通过地址栏传值时出现乱码的两种解决方法 ref和out的区别在c#中 总结

    史上最全的CSS hack方式一览 2013年09月28日 15:57:08 阅读数:175473 做前端多年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况.基于此,某些情况我 ...

  5. [APM] 解读APM技术分类和实现方式

    在讲了APM的历史.作用和实际案例之后,下面我们来了解一下APM技术分类和实现方式以及它未来的发展趋势.在这之前,我们首先需要了解一下典型的互联网或移动互联网应用的整个应用交付链. 图1 上面这张示意 ...

  6. java 中linq 的使用方式 筛选 查找 去重

    1.筛选 $.Enumerable.From(value).Where(function(x) {//value 为被操作的内容 return x.name == name;//第一个name为val ...

  7. Oracle并行更新的两种方式(merge/update内联视图)

    对于Oracle的两表联合更新的场景(有A.B两表,以A.id=B.id关联,根据B表中的记录更新A表中的相应字段),一般有update内联视图和merge两种方式,下面举例介绍:   创建用例表: ...

  8. 获取【请求体】数据的3种方式(精)(文末代码) request.getInputStream() request.getInputStream() request.getReader()

    application/x- www-form-urlencoded是Post请求默认的请求体内容类型,也是form表单默认的类型.Servlet API规范中对该类型的请求内容提供了request. ...

  9. 关于Eratosthenes筛子算法筛选小于n的素数的理解

    今天在学习Java核心技术第九章集合框架中的BitSet时,遇到了这个算法.Eratosthenes筛子算法时一个查找素数的方法,这并不是查找素数的最好方法,但是测试编译程序性能的一种流行的基准. 一 ...

随机推荐

  1. 2: Eclipse反编译工具Jad及插件JadClipse配置

    Jad是一个Java的一个反编译工具,是用命令行执行,和通常JDK自带的java,javac命令是一样的.不过因为是控制台运行,所以用起来不太方便.不过幸好有一个eclipse的插件JadClipse ...

  2. C++11并发——多线程lock_gurad ,unique_lock (三)

    http://www.cnblogs.com/haippy/p/3346477.html struct defer_lock_t {}; 该类型的常量对象 defer_lock(defer_lock ...

  3. 洛谷 P1020 导弹拦截(dp+最长上升子序列变形)

    传送门:Problem 1020 https://www.cnblogs.com/violet-acmer/p/9852294.html 讲解此题前,先谈谈何为最长上升子序列,以及求法: 一.相关概念 ...

  4. 【bzoj5161】最长上升子序列 状压dp+打表

    题目描述 现在有一个长度为n的随机排列,求它的最长上升子序列长度的期望. 为了避免精度误差,你只需要输出答案模998244353的余数. 输入 输入只包含一个正整数n.N<=28 输出 输出只包 ...

  5. H5新特性之拖拽文件

    H5新增了drag事件,在H5中拖拽是十分常见的. 可以拖拽的分为页面内的和页面外的 页面内的一般默认可以拖拽的是img和a标签 页面外的常指的是文件 上代码吧~ let zoom = documen ...

  6. Linux 内核 hlist 详解

    在Linux内核中,hlist(哈希链表)使用非常广泛.本文将对其数据结构和核心函数进行分析. 和hlist相关的数据结构有两个:hlist_head 和 hlist_node //hash桶的头结点 ...

  7. 客户端连接linux经常间隔性断开链接【转】

    起因 在使用SecureCRT通过telnet或SSH访问linux时,总是出现过段时间操作就会断开连接提示重连的问题.起初以为是网络不稳定造成的,但我测试发现在服务器端一直可以ping通客户端IP, ...

  8. golang数组声明

    格式 初始化数组 {}中的元素数不能大于[]中的数字,并且长度在初始化后不能改变,定义数组时需指定长度 ... var arrName [num]type = [num]type{value, val ...

  9. js中常用事件

    鼠标事件 /* onclick:点击某个对象时触发 ondblclick:双击某个对象时触发 onmouseover:鼠标移入某个元素时触发 onmouseout:鼠标移出某个元素时触发 onmous ...

  10. Tomcat中配置URIEncoding="UTF-8"来处理中文的方法

    http://www.cnblogs.com/seabird1979/p/4837237.htmlTomcat中配置URIEncoding="UTF-8"来处理中文的处理打开 se ...