Lua协同程序

Lua协同程序

协同程序本质上是协作的,它允许两种或多种方法以受控方式执行。 使用协同程序,在任何给定时间,只有一个协同程序运行,并且此运行协程仅在显式请求暂停时暂停执行。

上述定义可能看起来含糊不清。 假设有两种方法,一种是主程序方法,另一种是协程。 当使用resume函数调用一个协程时,它会开始执行,当调用yield函数时,它会暂停执行。 同样的协同程序可以继续执行另一个恢复函数调用,协同程序就会暂停。 该过程可以持续到协程执行结束。

协同程序函数

下表列出了Lua中协程的所有可用函数及其相应的用法。

编号 方法 作用或目的
1 coroutine.create (f) 使用函数f创建一个新的协同程序,并返回thread类型的对象。
2 coroutine.resume (co [, val1, ...]) 恢复协程co并传递参数(如果有的话)。它返回操作状态和可选的其他返回值。
3 coroutine.running () 如果在主线程中调用,则返回正在运行的协同程序或nil
4 coroutine.status (co) 根据协同程序的状态返回runningnormalsuspendeddead中的一个值。
5 coroutine.wrap (f) coroutine.create一样,coroutine.wrap函数也会创建一个协同程序,但它不会返回协同程序本身,而是返回一个函数,当调用它时,它会恢复协同程序。
6 coroutine.yield (...) 暂停正在运行的协同程序。 传递给此方法的参数充当resume函数的附加返回值。

示例
下面来看一个例子,通过此示例来理解协同程序的概念。

co = coroutine.create(function (value1,value2)
   local tempvar3 = 10
   print("coroutine section 1", value1, value2, tempvar3)

   local tempvar1 = coroutine.yield(value1+1,value2+1)
   tempvar3 = tempvar3 + value1
   print("coroutine section 2",tempvar1 ,tempvar2, tempvar3)

   local tempvar1, tempvar2= coroutine.yield(value1+value2, value1-value2)
   tempvar3 = tempvar3 + value1
   print("coroutine section 3",tempvar1,tempvar2, tempvar3)
   return value2, "end"

end)

print("main", coroutine.resume(co, 3, 2))
print("main", coroutine.resume(co, 12,14))
print("main", coroutine.resume(co, 5, 6))
print("main", coroutine.resume(co, 10, 20))

当运行上面的程序时,将得到以下输出 -

coroutine section 1    3    2    10
main    true    4    3
coroutine section 2    12    nil    13
main    true    5    1
coroutine section 3    5    6    16
main    true    2    end
main    false    cannot resume dead coroutine

上面的例子是实现什么功能?

如前所述,使用resume函数来启动操作和yield函数来停止操作。 此外,可以看到coroutine的恢复功能接收到多个返回值。

  • 首先,创建一个协同程序并分配给变量名称co,协同程序将两个变量作为参数。
  • 当调用第一个恢复函数时,值32保留在临时变量value1value2中,直到协程结束。
  • 使用了一个变量tempvar3,它最初值是10,并且通过后续的协程调用更新为1316,在整个协程的执行过程中value1的值保持为3
  • 第一个coroutine.yield将两个值43返回到resume函数,通过更新yield语句中的输入参数为32。 它还接收协程执行的true/false状态。
  • 关于协同程序的另一个问题是,在上面的例子中如何处理下一个resume调用的句子; 可以看到变量coroutine.yield接收下一个调用参数,它提供了一种强大的方法,可以通过保留现有的参数值来进行新的操作。
  • 最后,当协程中的所有语句都执行后,后续调用将返回false并且响应语句为:cannot resume dead coroutine

另一个协同程序示例

下面来看一个简单的协同程序示例,它使用yield函数和resume函数返回15之间的数字。 如果不可用它会创建协程,或者恢复现有的协程。

function getNumber()
   local function getNumberHelper()
      co = coroutine.create(function ()
      coroutine.yield(1)
      coroutine.yield(2)
      coroutine.yield(3)
      coroutine.yield(4)
      coroutine.yield(5)
      end)
      return co
   end

   if(numberHelper) then
      status, number = coroutine.resume(numberHelper);

      if coroutine.status(numberHelper) == "dead" then
         numberHelper = getNumberHelper()
         status, number = coroutine.resume(numberHelper);
      end

      return number
   else
      numberHelper = getNumberHelper()
      status, number = coroutine.resume(numberHelper);
      return number
   end

end

for index = 1, 10 do
   print(index, getNumber())
end

当运行上面的程序时,将得到以下输出。

1    1
2    2
3    3
4    4
5    5
6    1
7    2
8    3
9    4
10    5

通常会将协同程序与多路程序设计语言的线程进行比较,但需要了解协同程序具有类似的线程功能,但协同程序一次只执行一个程序,并且永远不会同时执行。

通过暂时保留某些信息来控制程序执行顺序以满足需求。 使用带有协同程序的全局变量为协同程序提供了更大的灵活性。