Lua学习笔记-9.2章-管道和过滤器

1、producer-consumer问题

couroutine的一个典型的例子就是producer-consumer问题。假设有这样两个函数,一个不停的produce一些值出来(例如从一个file中不停地读),另一个不断地consume这些值(例如,写入到另一个file中)。这两个函数的样子应该如下:

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

这两个函数都不停的在执行,那么问题来了,怎么来匹配send和recv呢?究竟谁先谁后呢?两者各自有自己的主循环。

采用resume-yield进行解决:

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 consumer (prod)
    while true do
        local x = receive(prod) -- receive from producer
        io.write(x, "\n") -- consume new value
    end
end

p = producer()
consumer(p)

当一个协同调用yield时并不会进入一个新的函数,取而代之的是返回一个未决的resume的调用。相似的,调用resume时也不会开始一个新的函数而是返回yield的调用。
这种性质正是我们所需要的,与使得send-receive协同工作的方式是一致的。receive唤醒生产者生产新值,send把产生的值送给消费者消费。

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

2、增加过滤器对中间数据进行处理
过滤器指在生产者与消费者之间,可以对数据进行某些转换处理。过滤器在同一时间既是生产者又是消费者,他请求生产者生产值并且转换格式后传给消费者,修改上面的代码加入过滤器。

先看一下没加filter之前的逻辑,基本就是producer去send, send to consumer, consumer去recv, recv from producer。加了filter之后呢,因为filter需要对data做一些转换操作,因此这时的逻辑为, producer去send,send to filter,filter去recv, recv from producer,     filter去send,send to consumer , consumer去recv,recv from 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() -- produce new value
            send(x)
        end
    end)
end
  
function consumer (prod)
    while true do
        local x = receive(prod) -- receive from producer
		if x then
			io.write(x, "\n") -- consume new value
		else
			break
		end
    end
end

function filter(prod)                                                                                                              
    return coroutine.create(function ()
        for line = 1, math.huge do
            local x = receive(prod)
            x = string.format('%5d %s', line, x)--格式化字符串,
	    -- x = string.format('%05d %s', line, x)--输出不足5位以0进行补齐
            send(x)
        end 
    end)
end

p = producer()
f = filter(p)
consumer(f)

Lua学习笔记-9.2章-管道和过滤器_第1张图片

看完上面的例子,有点儿类似unix中的pipe。使用pipe,每个task得以在各自的process里执行,而是用coroutine,每个task在各自的coroutine中执行。pipe在writer(producer)和reader(consumer)之间提供了一个buffer,因此相对的运行速度还是相当可以的。这个是pipe很重要的一个特性,因为process间通信,代价还是有点大的。使用coroutine,不同task之间的切换成本更小,基本上也就是一个函数调用,因此,writer和reader几乎可以说是齐头并进了啊。

你可能感兴趣的:(Lua)