Lua管道和过滤器

管道和过滤器

协同最有代表性的作用是用来描述生产者-消费者问题。我们假定有一个函数在不断的生产值(比如从文件中读取),另一个函数不断的消费这些值(比如写到另一文件中),这两个函数如下:

function producer () 
while true do
 local x = io.read() -- produce new value 
 send(x) -- send to consumer 
end 
end 
function consumer () 
while true do
 local x = receive() -- receive from producer 
 io.write(x, "\n") -- consume new value 
end 
end 

(例子中生产者和消费者都在不停的循环,修改一下使得没有数据的时候他们停下来并不困难),问题在于如何使得 receive 和 send 协同工作。只是一个典型的谁拥有住循环的情况,生产者和消费者都处在活动状态,都有自己的主循环,都认为另一方是可调用的服务。对于这种特殊的情况,可以改变一个函数的结构解除循环,使其作为被动的接受。然而这种改变在某些特定的实际情况下可能并不简单。

协同为解决这种问题提供了理想的方法,因为调用者与被调用者之间的 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() -- produce new value 
 send(x) 
end 
end) 

这种设计下,开始时调用消费者,当消费者需要值时他唤起生产者生产值,生产者生产值后停止直到消费者再次请求。我们称这种设计为消费者驱动的设计。

我们可以使用过滤器扩展这个涉及,过滤器指在生产者与消费者之间,可以对数据进行某些转换处理。过滤器在同一时间既是生产者又是消费者,他请求生产者生产值并且转换格式后传给消费者,我们修改上面的代码加入过滤器(每一行前面加上行号)。完整的代码如下:

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() -- produce new value 
 send(x) 
 end 
end) 
end 
function filter (prod) 
return coroutine.create(function () 
 local line = 1 
 while true do
 local x = receive(prod) -- get new value 
 x = string.format("%5d %s", line, x) 
 send(x) -- send it to consumer 
 line = line + 1 
 end 
end) 
end 
function consumer (prod) 
while true do
 local x = receive(prod) -- get new value 
 io.write(x, "\n") -- consume new value 
end 
end 
可以调用:
p = producer() 
f = filter(p) 
consumer(f) 
或者:
consumer(filter(producer())) 

看完上面这个例子你可能很自然的想到 UNIX 的管道,协同是一种非抢占式的多线程。管道的方式下,每一个任务在独立的进程中运行,而协同方式下,每个任务运行在独立的协同代码中。管道在读(consumer)与写(producer)之间提供了一个缓冲,因此两者相关的的速度没有什么限制,在上下文管道中这是非常重要的,因为在进程间的切换代价是很高的。协同模式下,任务间的切换代价较小,与函数调用相当,因此读写可以很好的协同处理。

你可能感兴趣的:(Lua管道和过滤器)