四、Lua协同程序

一、协同程序基础

  1.什么是协同程序

  协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈、局部变量和指令指针(即可以保存变量的值和状态),同时又与其他协同程序共享全局变量和其他大部分东西。

  与线程的区别是具有多个线程的程序可以同时运行几个线程,而程序任意时刻只能运行一个协同程序,并且协同程序只有被显示地(执行挂起函数)要求挂起才会暂停。

  2.四种状态

  (1)挂起(suspended):创建时或执行了操作coroutine.yield()

  (2)运行(running):执行了coroutine.resume()

  (3)死亡(dead):最后一次调用resume时,协同程序的内容已执行完毕,并且已经返回。resume状态为dead的协同程序将返回false及错误消息,不再执行主函数体。

  (4)正常(normal):当一个协同程序A唤醒另一个协同程序B时,A就处于一种特殊状态——既不是挂机状态(A不能继续执行),也不是运行状态(B在运行)。

  3.相关函数(都存放在全局table coroutine中)

  (1)create(fun):用于创建协同程序,只有一个参数,就是一个函数(一般为匿名函数),函数中的代码就是协同程序将要执行的内容。create返回值的类型为thread。创建后的协同程序处于挂起状态。

  (2)resume(co,...):启动或再次启动协同程序。参数列表中:co表示将要被启动的协同程序,后面为变长的可选参数。

  (3)yield(...):使正在运行的协同程序挂起,之后可以再恢复运行(调用resume)。只能被协同程序的主函数调用(即在协同程序执行的内容中调用)。参数列表为可选的变长参数。

  (4)status(co):检查协同程序的当前状态,返回值为四中状态中的一种。参数co为待检查的协同程序。

  4.有用机制:通过一对resume-yield交换数据

  原理:

  (1)所有传递给resume的额外参数(...)都将视为协同程序主函数的参数。如:

          co = coroutine.create ( function(a,b)  print("co:",a,b)  end )  --创建协同程序

          coroutine.resume(co,10,20)  --> co:10  20  --启动协同程序,其中10,20就是额外参数,传递给了协同程序的主函数(匿名函数)

  (2)resume的返回值中包含传递给yield的所有参数。如:

               co = coroutine.create( function(a,b)

              coroutine.yield(a+b,a-b)  --在协同程序的主函数中直接调用yield

              i = i+1  --有语法错误

            end )          

          第一次调用resume:print(coroutine.resume(co,20,10)) -->true  30  10   --第一个返回值为true表示协同程序执行完毕或执行到yield之前没有发生错误,后面的返回值就是传递给yield的参数。   

          第二次调用resume:print(coroutine.resume(co,20,10)) -->false,attempt to perform arithmetic on global 'i' (a nil value) --resume 第一个返回值为true并不能说明协同程序没有错误。

  (3)yield的返回值就是对应resume传入的额外参数。如:

          co = coroutine.create( function() print("co:",coroutine.yield()) end)

          coroutine.resume(co) -->   --resume中没有额外参数,则yield不会返回值,协同程序挂起

  (4)当协同程序结束时,它的主函数返回的值都将作为resume的返回值。如:

          co = coroutine.create( function() return 1,2 end )

          print(coroutine.resume(co)) -->true 1  2 

二、协同程序的应用

  1.生产者-消费者

  (1)生产者-消费者一般程序表示:

      function producer() --生产者                                    function consumer() --消费者

        while true do                     while true do   

          local x = io.read()                  local x = receive()

          send(x)                       io.write(x," ")

        end                           end 

      end                          end 

      问题关键:如何将send和receive匹配起来?

  (2)生产者-消费者协同程序实现   

      function send(x) --协同程序主函数中被调用,因而可以调用yield
        coroutine.yield(x) --参数x将作为resume的返回值
      end

      producer = coroutine.create( function()  --在这里可以理解为生产者
            while true do
              local x = io.read() --输入,这里可理解为生产的东西
              send(x)
            end
          end)

      function receive()  --在这里可以理解为消费者
        local status, value = coroutine.resume(producer) --启动协同程序
        return value
      end

      程序通过调用消费者receive()来唤醒生产者(即协同程序),然后通过将“生产的”内容x作为参数传递给yield,最后作为resume的返回值,赋给value(即消费者得到生产者生产的东西)

      实质上利用了一对resume-yield交换数据。

原文地址:https://www.cnblogs.com/heyongqi/p/5170031.html