【深刻Lua】理解Lua中最强大的特性-coroutine(协程)[转]

 

###coroutine基础多线程

Lua所支持的协程全称被称做协同式多线程(collaborative multithreading)。Lua为每一个coroutine提供一个独立的运行线路。然而和多线程不一样的地方就是,coroutine只有在显式调用yield函数后才被挂起,同一时间内只有一个协程正在运行。异步

Lua将它的协程函数都放进了coroutine这个表里,其中主要的函数以下函数

表格

摘取一段云风的代码来详尽解释协程的工做机制,在这段代码中,展现了main thread和协程co之间的交互:oop

<!-- lang: lua -->
function foo(a)
    print("foo", a)
    return coroutine.yield(2 * a)
end

co = coroutine.create(function ( a, b )
    print("co-body", a, b)
    local r = foo(a + 1)
    print("co-body", r)
    local r, s = coroutine.yield(a + b, a - b)
    print("co-body", r, s)
    return b, "end"
end)

print("main", coroutine.resume(co, 1, 10))
print("main", coroutine.resume(co, "r"))
print("main", coroutine.resume(co, "x", "y"))
print("main", coroutine.resume(co, "x", "y"))

 

下面是运行结果lua

co-body 1 10
foo 2
main true 4
co-body r
main true 11, -9
co-body x y
main false 10 end
main false cannot resume dead coroutine

 

###coroutine和subroutine(子例程)的比较spa

子例程的起始处是惟一的入口点,一旦return就完成了子程序的执行,子程序的一个实例只会运行一次。.net

可是协程不同,协程能够使用yield来切换到其余协程,而后再经过resume方法重入**(reenter)到上次调用yield的地方,而且把resume的参数当成返回值传给了要重入(reenter)**的协程。可是coroutine的状态都没有被改变,就像一个能够屡次返回的subroutine线程

协程的精妙之处就在于协做这一律念,下面咱们用生产者和消费者问题来演示一下协程的基本应用。注意:下面的伪码是用Lua的思想写的code

var q = queue()

 

生产者的伪码协程

loop
    while q is not full
        create product
        add the items to q
    resume to consumer

 

消费者的伪码

loop
    while q is not empty
        consume product
        remove the items from q
    yield

 

###coroutine的和callback的比较

coroutine常常被用来和callback进行比较,由于一般来讲,coroutine和callback能够实现相同的功能,即异步通讯,好比说下面的这个例子:

<!-- lang: lua -->
bob.walkto(jane)
bob.say("hello")
jane.say("hello")

 

看起来好像是对的,但实际上因为这几个动做walkto,say都是须要必定时间才能作完的,因此这段程序若是这样写的话,就会致使bob一边走一边对jane说hello,而后在同时jane也对bob说hello,致使整个流程十分混乱。

若是使用回调来实现的话,代码示例以下:

<!-- lang: lua -->
bob.walto(function (  )
    bob.say(function (  )
        jane.say("hello")
    end,"hello")
end, jane)

 

即walto函数回调say函数,say函数再回调下一个say函数,这样回调看起来十分混乱,让人没法一下看出这段代码的意义.

若是用coroutine的话,能够使用以下写法:

<!-- lang: lua -->
co = coroutine.create(function (  )
    local current = coroutine.running
    bob.walto(function (  )
        coroutine.resume(current)
    end, jane)
    coroutine.yield()
    bob.say(function (  )
        coroutine.resume(current)
    end, "hello")
    coroutine.yield()
    jane.say("hello"end)

coroutine.resume(co)

 

在上述代码中,一旦一个异步函数被调用,协程就会使用coroutine.yield()方法将该协程暂时悬挂起来,在相应的回调函数中加上coroutine.resume(current),使其返回目前正在执行的协程中。

可是,上述代码中有许多重复的地方,因此能够经过将封装的方式将重复代码封装起来

<!-- lang: lua -->
function runAsyncFunc( func, ... )
    local current = coroutine.running
    func(function (  )
        coroutine.resume(current)
    end, ...)
    coroutine.yield()
end

coroutine.create(function (  )
    runAsyncFunc(bob.walkto, jane)
    runAsyncFunc(bob.say, "hello")
    jane.say("hello")
end)

coroutine.resume(co)

 

这样就不须要改变从前的全部回调函数,便可经过携程的方式解决异步调用的问题,使得代码的结构很是清晰。

 

 

 

 

转自:https://my.oschina.net/wangxuanyihaha/blog/186401