《Lua程序设计》9.2 管道(pipe)与过滤器(filter) 包含使用协同函数实现“生产者——消费者”问题的实例代码
一个关于协同程序的经典示例是“生产者-消费者”问题。这其中涉及到两个函数,一个函数不断地产生值(比如从一个文件中读取值),另一个则不断地消费这些值(比如将这些值写到另一个文件)。通常,这两个函数大致是这样的:
- function producer ()
- while true do
- local x = io.read() -- 产生新的值
- send(x) -- 发送给消费者
- end
- end
- function consumer ()
- while true do
- local x = receive() -- 从生产者接收值
- io.write(x, "\n") -- 消费新的值
- end
- end
这里有一个问题是如何将send与receive匹配起来。这是一个典型的“谁具有主循环(who-has-the-main-loop)”的问题。由于生产者和消费者都处于活动状态,它们个字具有一个朱迅欢,并且都将对方视为一个磕掉用的服务。
协同程序被称为是一种匹配生产者和消费者的理想工具,一对resume-yield完全一改典型的调用者与被调用者之间的关系。当一个协同程序调用yield时,他不是进入了一个新的函数,而是从一个悬而未决的resume调用中返回。同样地,对于resume的调用也不会启动一个新函数,而是从一次yield调用中返回。这项特性正可用于匹配send和receive,这两者都认为自己是主动方,对方是被动方。receive唤醒生产者的执行,促使其能产出一个新值。而send则产生一个新值返还给消费者:
- function receive ()
- local status, value = coroutine.resume(producer)
- return value
- end
- function send (x)
- coroutine.yield(x)
- end
因此,生产者现在一定是一个协同程序:
- producer = coroutine.create(
- function ()
- while true do
- local x = io.read() -- 产生新的值
- send(x) -- 发送给消费者
- end
- end
- end)
在这种设计中,程序通过调用消费者来启动。当消费者需要一个新值时,它唤醒生产者。生产者返回一个新值后停止运行,并等待消费者的自此唤醒。将这种设计称为“消费者驱动(consumer-driven)”。
还可以扩展上述功能,实现“过滤器(filter)”。过滤器是一种位于生产者和消费者之间的处理功能,可用于对数据的一些变幻。
过滤器既是一个消费者也会死一个生产者,它唤醒一个生产者促使其产生新值,然后又将变换后的值传递给消费者。例如可以在前面代码中添加一个过滤器,在每行起始处插入一个行号。代码如下:
- function receive (prod)
- local status, value = coroutine.resume(prod)
- return value
- end
- function send (x)
- coroutine.yield(x)
- end
- function producer ()
- return coroutine.create(function ()
- while true do
- local x = io.read() -- 产生新值
- send(x)
- end
- end)
- end
- function filter (prod)
- return coroutine.create(function ()
- for line = ,math.huge do
- local x = receive(prod) -- 获取新值
- x = string.format("%5d %s", line, x)
- send(x) -- 将新值发送给消费者
- end
- end)
- end
- function consumer (prod)
- while true do
- local x = receive(prod)
- io.write(x, "\n")
- end
- end
接下来创建运行代码就非常简单了,只需将这些函数串联起来,然后启动消费者:
- p = producer()
- f = filter(p)
- consumer(f)
或者,更简单地写为:
- consumer(filter(producer()))
如果接触过UNIX的pipe(管道),那么本例的内容就不会很陌生。毕竟,协同程序也是一种(非抢先的)多线程。在pipe中没想任务都在各自独立的进程中运行,而在协同程序中每项任务都在各自独立的协同程序中运行。pipe在writer(消费者)与reader(生产者)之间提供一个缓冲器,因此它们的运行速度允许存在一定差异。值得注意的是,在pipe中进程间的切换代价恨到。而在协同程序中,切换代价则小得多,因此writer和reader可以彼此协作地运行。
----------
完整程序示例:
- function receive (prod)
- local status, value = coroutine.resume(prod)
- return value
- end
- function send (x)
- coroutine.yield(x)
- end
- function producer ()
- return coroutine.create(function ()
- while true do
- local x = io.read() -- 产生新值
- send(x)
- end
- end)
- end
- function filter (prod)
- return coroutine.create(function ()
- for line = ,math.huge do
- local x = receive(prod) -- 获取新值
- x = string.format("%5d %s", line, x)
- send(x) -- 将新值发送给消费者
- end
- end)
- end
- function consumer (prod)
- while true do
- local x = receive(prod)
- io.write(x, "\n")
- end
- end
- p = producer()
- f = filter(p)
- consumer(f)
- -- consumer(filter(producer()))
《Lua程序设计》9.2 管道(pipe)与过滤器(filter) 包含使用协同函数实现“生产者——消费者”问题的实例代码的更多相关文章
- Lua 程序设计 (Roberto,Ierusalimschy 著)
1 开始 2 类型与值 3 表达式 4 语句 5 函数 6 深入函数 7 迭代器与泛型for 8 编译,执行与错误 9 协同程序(coroutine) 10 完整的示例 11 数据结构 12 数据文件 ...
- Python--线程队列(queue)、multiprocessing模块(进程对列Queue、管道(pipe)、进程池)、协程
队列(queue) 队列只在多线程里有意义,是一种线程安全的数据结构. get与put方法 ''' 创建一个“队列”对象 import queue q = queue.Queue(maxsize = ...
- [ionic开源项目教程] - 第6讲 过滤器filter的使用
过滤器filter的使用 1.回顾 再熟悉一下tab1.html的代码: <div class="list"> <a ng-repeat="item i ...
- AngularJS过滤器filter入门
在开发中,经常会遇到这样的场景 如用户的性别分为“男”和“女”,在数据库中保存的值为1和0,用户在查看自己的性别时后端返回的值自然是1或0,前端要转换为“男”或“女”再显示出来: 如我要换个羽毛球拍, ...
- 关于Lua程序设计{读书笔记}
1.lua中的标识符可以是由任意字母.数字和下划线构成的字符串,但不能以数字开头.2.lua将通常类似"_VALUE"的标识符作为保留标识符3.lua的保留字 and break ...
- 管道Pipe
管道Pipe java.nio.channels包中含有一个名为Pipe(管道)的类.广义上讲,管道就是一个用来在两个实体之间单向传输数据的导管.管道的概念对于Unix(和类Unix)操作系统的用户来 ...
- Linux简单程序实例(GNU工具链,进程,线程,无名管道pipe,基于fd的文件操作,信号,scoket)
一, GNU工具链简介: (1)编译代码步骤: 预处理 -> 编译 -> 汇编 -> 链接: 预处理:去掉注释,进行宏替换,头文件包含等工作: gcc -E test.c -o te ...
- 【IPC第二个进程间通信】管道Pipe
IPC进程间通信+管道Pipe IPC(Inter-Process Communication,进程间通信). 管道用于进程间共享数据,事实上质是共享内存 ...
- (转)Windows管道(Pipe)重定向stdout,stderr,stdin
参考: http://qiusuoge.com/11496.html http://www.cnblogs.com/BoyXiao/archive/2011/01/01/1923828.html st ...
随机推荐
- tensorflow模型量化压缩
参考 https://blog.csdn.net/xygl2009/article/details/80596392 https://blog.csdn.net/xsfl1234/article/de ...
- L0、L1及L2范数
L1归一化和L2归一化范数的详解和区别 https://blog.csdn.net/u014381600/article/details/54341317 深度学习——L0.L1及L2范数 https ...
- Python 自定义异常处理Error函数
#!/usr/bin/env python # -*- coding:utf-8 -*- CODEMSG = { 2000: u"True", 4000: u"客户上传的 ...
- kali 安装KVM教程---》给自己的笔记
⦁ 查看系统环境支持 egrep 'svm|vmx' /proc/cpuinfo 如果不支持则是 没有任何输出如果支持有类似下面的输出: ⦁ 安装软件 sudo apt-get install kvm ...
- e824. 获得和设置JSplitPane中的子组件
// Create a left-right split pane JSplitPane hpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, lef ...
- CentOS 6.x安装配置MongoDB 3.4.x
说明: 操作系统:CentOS 5.X 64位 IP地址:192.168.21.128 实现目的: 安装配置MongoDB数据库 具体操作: 一.关闭SElinux.配置防火墙 1.vi /etc/s ...
- install sun-java6-jdk in ubuntu12.04
http://blog.sina.com.cn/s/blog_6296abc601018p86.html 在文件/etc/apt/sources.list 的最后添加下面的源: deb http:// ...
- Cross-Site Scripting XSS 跨站攻击全攻略 分类: 系统架构 2015-07-08 12:25 21人阅读 评论(2) 收藏
原文:http://a1pass.blog.163.com/blog/static/2971373220087295449497/ 题记:这是我在<黑客X档案>08年第5期发表的一篇文章, ...
- RMAN命令总结
一,RMANR 连接到库 1),连接本地数据库 a,如果本地有多少实例,则需要设置环境变量ORACLE_SID, windows 平台: set ORACLE_SID= INSTANACE NAME ...
- 大数据学习笔记01-HDFS-集群安装
安装 下载 Hadoop,以2.7.5版本为例 在虚拟机上创建目录bigdata,即执行mkdir bigdata 上传到master机器节点的目录~/bigdata下(可以用FileZilla等ft ...