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


    ###coroutine基础

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

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

    表格

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

    <!-- 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"))

    下面是运行结果

    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(子例程)的比较

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

    但是协程不一样,协程可以使用yield来切换到其他协程,然后再通过resume方法重入**(reenter)到上次调用yield的地方,并且把resume的参数当成返回值传给了要重入(reenter)**的协程。但是coroutine的状态都没有被改变,就像一个可以多次返回的subroutine

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

    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

  • 相关阅读:
    springmvc 配置多视图,返回jsp,velocity,freeMarker,tiles(模板)等等
    spring,mybatis,多数据源配置
    springMVC分页,interceptor实现
    springMVC全局Exception异常处理SimpleMappingExceptionResolver
    百度FIS入门
    如何安装nodejs
    如何高效部署前端代码,如css,js...
    javascript url几种编码方式
    【转】RBAC权限管理
    elasticsearch 之IK分词器安装
  • 原文地址:https://www.cnblogs.com/xiand/p/8950319.html
Copyright © 2020-2023  润新知