zoukankan      html  css  js  c++  java
  • lua编程之协程介绍

    一,lua协程简介

    协程(coroutine),意思就是协作的例程,最早由Melvin Conway在1963年提出并实现。跟主流程序语言中的线程不一样,线程属于侵入式组件,线程实现的系统称之为抢占式多任务系统,而协程实现的多任务系统成为协作式多任务系统。线程由于缺乏yield语义,所以运行过程中不可避免需要调度,休眠挂起,上下文切换等系统开销,还需要小心使用同步机制保证多线程正常运行。而协程的运行指令系列是固定的,不需要同步机制,协程之间切换也只涉及到控制权的交换,相比较线程来说是非常轻便的。不过同一时刻可以有多个线程运行,但却只能有一个协程运行。

    协程具有两个非常重要的特性:

    1. 私有数据在协程的间断式运行期间一直有效

    2. 协程每次yield后让出控制权,下次被resume后从停止点开始继续执行

    通俗的说,比较像一个带有静态数据而且具有多个进入点和返回点的函数,下面通过一个简单例子看看这个性质:

    co = coroutine.create(
    function(a)
      print("a = "..a)
      c = coroutine.yield(a+1)
      print("c = "..c)
      return c*2
    end
    )
    _, b = coroutine.resume(co, 1)
    print("b = "..b)
    _, d = coroutine.resume(co, b )
    print("d = "..d)

    运行结果:

    a = 1

    b = 2

    c = 2

    d = 4

    协程co中除了一般函数具有的进入点(函数入口处)和返回点(函数结尾),在yield出还有一个进入点和返回点。主程序第一次resume时将1传给参数a,运行到yield时,协程返回控制权给主程序,并通过yield的参数提供返回值,于是b=a+1,再次resume时,主程序通过参数b将数据传给co的变量c,从而从第二个入口点进入函数,最后返回值c*2传给d。

    resume/yield语义实现的协程属于非对称协程,在非对称协程中,调用者和被调用者的关系是固定的,调用者通过resume将控制流转到被调用者,被调用者通过yield只能返回到调用者,而不能返回到其他协程。比如A resume B resume C, C yield只能到B,而不能到A或其他协程。

    世间万物都是对立又统一的,既然存在非对称协程,当然就存在对称协程。对称协程只有一个语义可以将控制流直接转到目的协程。

    非对称协程和对称协程的表达能力是一样的,lua中只实现了非对称协程,一个重要原因是lua是c实现的,非对称协程的调用与被调用关系与c的函数调用非常类似,方便lua和c的扩展编程。

    二,协程实战

    下面例子利用协程实现经典的生产者-消费者模型。

    count = 10
    productor = coroutine.create( 
      function () 
        i = 0 
        while(true) do 
          i = i+1 
          coroutine.yield(i) 
        end 
      end 
    ) 
    
    consumer = coroutine.create( 
    function(co) 
      n = 1 
      while(n<count) do 
      _, v = coroutine.resume(co) 
        print(v) 
        n = n+1 
      end 
    end 
    ) 
    
    coroutine.resume(consumer, productor)

    consumer启动productor,productor产生一个item后通过yield传回给consumer,然后挂起等待consumer下次resume,非常简单直观。

    协程的另一个重要作用是作为迭代器,依次访问数据结构的元素。下面代码展示了先序访问二叉树的方法。

    l = { 
      v = 1, 
      left = nil, 
      right = nil, 
    } 
    
    r = { 
      v = 2, 
      left = nil, 
      right = nil, 
    } 
    
    root = { 
      v = 3, 
      left = l, 
      right = r, 
    } 
    
    preorder = function(root) 
      if(root == nil) then return end 
      coroutine.yield(root.v) 
      preorder(root.left) 
      preorder(root.right) 
    end 
    
    preco = coroutine.create(preorder) 
    
    
    view = function(co, root) 
      state, v = coroutine.resume(co, root) 
      if(state == false) then return end 
      print(v) 
      while(true)  do 
        state, v = coroutine.resume(co) 
        if(state == false or v == nil) then return end 
        print(v) 
      end 
    end 
    view(preco, root)

    对于coroutine, 这里介绍了一个非常轻量级协程的实现原理,云风通过uconext实现了类似于lua的协程库,有兴趣的同学可以研究研究。

     

    reference:

    《coroutines in C》

  • 相关阅读:
    Java-IO流-简介
    Java-异常处理-自定义异常
    致橡树-舒婷
    js字符串/数组常用方法总结
    使用vue-cli4快速搭建vue项目demo
    使用vue-cli4快速搭建vue项目demo
    小白第一次用MacOS
    文字背景对比度contrast ratio的计算公式
    如何使用 v-model 绑定一个 computed 属性?
    Java基础--数组
  • 原文地址:https://www.cnblogs.com/coderkian/p/4052843.html
Copyright © 2011-2022 走看看