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. 二叉查找树(BST)、平衡二叉树(AVL树)

    二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点,都满足其左子树上所有结点的数据域均小于或等于根结点的数据域,右 ...

  2. 用Shell编写的俄罗斯方块代码

    用Shell编写的俄罗斯方块代码 不得不承认任何一门语言玩6了,啥都能搞出来啊,竟然用Shell编写出来了一个俄罗斯方块游戏的代码,很有意思,这个代码不是我写出来的,不过大家可以下载一下在window ...

  3. mysql命令备份和还原

    1.导出整个数据库 mysqldump -u用户名 -p密码  数据库名 > 导出的文件名  C:\Users\jack> mysqldump -uroot -pmysql sva_rec ...

  4. CM记录-选择合适的硬件

    hadoop的运行环境---hadoop/jvm/os/hw 原则1:主节点的可靠性要好于从节点:NameNode(Yarn-ResourceManager).DataNode(NodeManager ...

  5. Study 1 —— HTML5概述

    HTML5概述HTML是一种超文本标记语言,主要用于描述超文本中内容的显示方式.标记语言经过浏览器的解释和编译,虽然它本身不能显示在浏览器中,但在浏览器中可以正确显示HTML标记的内容.HTML5是一 ...

  6. Java中的回车换行符/n /r /t

    '\r'是回车,'\n'是换行,前者使光标到行首,后者使光标下移一格.通常用的Enter是两个加起来.下面转一篇文章. 回车和换行 今天,我总算搞清楚“回车”(carriage return)和“换行 ...

  7. bzoj千题计划299:bzoj1856: [Scoi2010]字符串

    http://www.lydsy.com/JudgeOnline/problem.php?id=1856 卡特兰数 从(1,1)走到(n,m),不能走y=x 上方的点,求方案数 从(1,1)走到(n, ...

  8. 浅谈分词算法(4)基于字的分词方法(CRF)

    目录 前言 目录 条件随机场(conditional random field CRF) 核心点 线性链条件随机场 简化形式 CRF分词 CRF VS HMM 代码实现 训练代码 实验结果 参考文献 ...

  9. bootstrap modal垂直居中 (转)

    根据博友的经验,总结后请使用方法一就行了 一,修改bootstrap.js 源码 原来的: Modal.prototype.adjustDialog = function () { ].scrollH ...

  10. u-boot移植(九)---代码修改---NAND

    一.NAND原理 NAND 无地址空间,地址和数据的发送都依赖于LDATA[0:7]这一串数据总线. 不看随机页编程,看到从高位到低位的页,总共分为64个页面,每个页的组成是2K + 64  个byt ...