Lua中的协同程序 coroutine

  Lua中的协程和多线程很类似,每个协程有本身的堆栈,本身的局部变量,能够经过yield-resume实如今协程间的切换。不一样之处是:Lua协程是非抢占式的多线程,必须手动在不一样的协程间切换,且同一时刻只能有一个协程在运行。而且Lua中的协程没法在外部将其中止,并且有可能致使程序阻塞。多线程

 

协同程序(Coroutine):函数

  三个状态:suspended(挂起,协同刚建立完成时或者yield以后)、running(运行)、dead(函数走完后的状态,这时候不能再从新resume)。spa

  coroutine.create(arg):根据一个函数建立一个协同程序,参数为一个函数线程

  coroutine.resume(co):使协同从挂起变为运行(1)激活coroutine,也就是让协程函数开始运行;(2)唤醒yield,使挂起的协同接着上次的地方继续运行。该函数能够传入参数设计

  coroutine.status(co):查看协同状态code

  coroutine.yield():使正在运行的协同挂起,能够传入参数协程

  resume函数的两种用途虽然都是使协同挂起,但仍是有些许差别的,看下面这个例子:blog

coroutineFunc = function (a, b) 
    for i = 1, 10 do
        print(i, a, b)
        coroutine.yield()
    end
end

co2 = coroutine.create(coroutineFunc)        --建立协同程序co2
coroutine.resume(co2, 100, 200)                -- 1 100 200 开启协同,传入参数用于初始化
coroutine.resume(co2)                        -- 2 100 200 
coroutine.resume(co2, 500, 600)                -- 3 100 200 继续协同,传入参数无效

co3 = coroutine.create(coroutineFunc)        --建立协同程序co3
coroutine.resume(co3, 300, 400)                -- 1 300 400 开启协同,传入参数用于初始化
coroutine.resume(co3)                        -- 2 300 400 
coroutine.resume(co3)                        -- 3 300 400 

   Lua中协同的强大能力,还在于经过resume-yield来交换数据:产品

  (1)resume把参数传给程序(至关于函数的参数调用);io

  (2)数据由yield传递给resume;

  (3)resume的参数传递给yield;

  (4)协同代码结束时的返回值,也会传给resume

   协同中的参数传递形势很灵活,必定要注意区分,在启动coroutine的时候,resume的参数是传给主程序的;在唤醒yield的时候,参数是传递给yield的。看下面这个例子:

co = coroutine.create(function (a, b) print("co", a, b, coroutine.yield()) end)
coroutine.resume(co, 1, 2)        --没输出结果,注意两个数字参数是传递给函数的
coroutine.resume(co, 3, 4, 5)        --co 1 2 3 4 5,这里的两个数字参数由resume传递给yield 

  Lua的协同称为不对称协同(asymmetric coroutines),指“挂起一个正在执行的协同函数”与“使一个被挂起的协同再次执行的函数”是不一样的,有些语言提供对称协同(symmetric coroutines),即便用同一个函数负责“执行与挂起间的状态切换”。

   注意:resume运行在保护模式下,所以,若是协同程序内部存在错误,Lua并不会抛出错误,而是将错误返回给resume函数。

   如下是我我的的一点理解:

  (1)resume能够理解为函数调用,而且能够传入参数,激活协同时,参数是传给程序的,唤醒yield时,参数是传递给yield的;

  (2)yield就至关因而一个特殊的return语句,只是它只是暂时性的返回(挂起),而且yield能够像return同样带有返回参数,这些参数是传递给resume的。

为了理解上面两句话的含义,咱们来看一下如何利用Coroutine来解决生产者——消费者问题的简单实现:

produceFunc = function()
    while true do
        local value = io.read()
        print("produce: ", value)
        coroutine.yield(value)        --返回生产的值
    end
end

consumer = function(p)
    while true do
        local status, value = coroutine.resume(p);        --唤醒生产者进行生产
        print("consume: ", value)
    end
end

--消费者驱动的设计,也就是消费者须要产品时找生产者请求,生产者完成生产后提供给消费者
producer = coroutine.create(produceFunc)
consumer(producer)

这是一种消费者驱动的设计,咱们能够看到resume操做的结果是等待一个yield的返回,这很像普通的函数调用,有木有。咱们还能够在生产消费环节之间加入一个中间处理的环节(过滤器):

produceFunc = function()
    while true do
        local value = io.read()
        print("produce: ", value)
        coroutine.yield(value)        --返回生产的值
    end
end

filteFunc = function(p)
    while true do
        local status, value = coroutine.resume(p);
        value = value *100            --放大一百倍
        coroutine.yield(value)
    end
end

consumer = function(f, p)
    while true do
        local status, value = coroutine.resume(f, p);        --唤醒生产者进行生产
        print("consume: ", value)
    end
end

--消费者驱动的设计,也就是消费者须要产品时找生产者请求,生产者完成生产后提供给消费者
producer = coroutine.create(produceFunc)
filter = coroutine.create(filteFunc)
consumer(filter, producer)

  能够看到,咱们在中间过滤器中将生产出的值放大了一百倍。

  经过这个例子应该很容易理解coroutine中如何利用resume-yield调用来进行值传递了,他们像“调用函数——返回值”同样的工做,也就是说resume像函数调用同样使用,yield像return语句同样使用。coroutine的灵活性也体如今这种经过resume-yield的值传递上。