博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
解读使用Daisy-chain(菊花链)方式筛选一定范围内素数的代码
阅读量:2241 次
发布时间:2019-05-09

本文共 4718 字,大约阅读时间需要 15 分钟。

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以内的所有素数 // 修改为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 @  0xc000050060xrange2: i =  2xrange2: i =  30.main - nums @  0xc0000500602filter - in @  0xc000050060filter - number:  21.main - nums @  0xc0000500c0filter - in @  0xc000050060 , i =  3放入输出信道 in @  0xc000050060 , number =  23filter - in @  0xc0000500c0filter - number:  31.main - nums @  0xc000050120xrange2: i =  4xrange2: i =  5filter - in @  0xc000050060 , i =  4filter - in @  0xc000050060 , i =  5放入输出信道 in @  0xc000050060 , number =  2filter - in @  0xc0000500c0 , i =  5放入输出信道 in @  0xc0000500c0 , number =  35filter - in @  0xc000050120filter - number:  5xrange2: i =  6xrange2: i =  7filter - in @  0xc000050060 , i =  6filter - in @  0xc000050060 , i =  7放入输出信道 in @  0xc000050060 , number =  2filter - in @  0xc0000500c0 , i =  7放入输出信道 in @  0xc0000500c0 , number =  3filter - in @  0xc000050120 , i =  7放入输出信道 in @  0xc000050120 , number =  51.main - nums @  0xc0000501807filter - in @  0xc000050180filter - number:  71.main - nums @  0xc00001c060xrange2: i =  8xrange2: i =  9filter - in @  0xc000050060 , i =  8filter - in @  0xc000050060 , i =  9放入输出信道 in @  0xc000050060 , number =  2filter - in @  0xc0000500c0 , i =  9xrange2: i =  10xrange2: i =  11filter - in @  0xc000050060 , i =  10filter - in @  0xc000050060 , i =  11放入输出信道 in @  0xc000050060 , number =  2filter - in @  0xc0000500c0 , i =  11放入输出信道 in @  0xc0000500c0 , number =  3filter - in @  0xc000050120 , i =  11放入输出信道 in @  0xc000050120 , number =  5filter - 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.

2.

 

转载于:https://www.cnblogs.com/luo630/p/9720696.html

你可能感兴趣的文章
Leetcode C++《热题 Hot 100-24》5.最长回文子串
查看>>
Leetcode C++《热题 Hot 100-28》19.删除链表的倒数第N个节点
查看>>
Leetcode C++《热题 Hot 100-29》22.括号生成
查看>>
阿里云《云原生》公开课笔记 第二章 容器基本概念
查看>>
阿里云《云原生》公开课笔记 第三章 kubernetes核心概念
查看>>
阿里云《云原生》公开课笔记 第四章 理解Pod和容器设计模式
查看>>
阿里云《云原生》公开课笔记 第五章 应用编排与管理
查看>>
阿里云《云原生》公开课笔记 第六章 应用编排与管理:Deployment
查看>>
阿里云《云原生》公开课笔记 第七章 应用编排与管理:Job和DaemonSet
查看>>
阿里云《云原生》公开课笔记 第八章 应用配置管理
查看>>
阿里云《云原生》公开课笔记 第九章 应用存储和持久化数据卷:核心知识
查看>>
linux系统 阿里云源
查看>>
国内外helm源记录
查看>>
牛客网题目1:最大数
查看>>
散落人间知识点记录one
查看>>
Leetcode C++ 随手刷 547.朋友圈
查看>>
手抄笔记:深入理解linux内核-1
查看>>
内存堆与栈
查看>>
Leetcode C++《每日一题》20200621 124.二叉树的最大路径和
查看>>
Leetcode C++《每日一题》20200622 面试题 16.18. 模式匹配
查看>>