什么是Lua协同程序?和线程有什么区别?

大家好,我是袁庭新。本文将详细介绍Lua协同程序,它是一种轻量级线程,能够支持任务的挂起和恢复执行,实现非抢占式的多任务处理。

1.Lua协同程序概述

1.1 Lua协同程序介绍

Lua的协同程序(coroutine)是一种轻量级的并发机制,它允许在单线程环境中实现多任务处理。与传统的多线程不同,Lua的协同程序是非抢占式的,这意味着它们不会被强制中断,而是必须显式地选择暂停执行。每个协同程序拥有自己独立的栈、局部变量和指令指针,但与其他协同程序共享全局变量和其他大部分资源。

从本质上说,协同程序类似线程,不过它们的调度方式有很大差异。线程是由操作系统来安排什么时候开始、暂停或者结束,就像是火车按照铁路调度系统的安排运行。而协同程序是由程序自己说了算,就好比是你自己开着车,可以随时决定停车或者继续走,这就提供了一种非抢占式的多任务处理模式。在单线程的Lua环境下,协同程序让程序能够营造出一种好像多个任务同时在进行的感觉。

1.2 协同程序的主要函数

Lua协同程序的主要函数总结如下表所示。

|--------------------------------------|---------------------------------------------------------------------------------------------|
| 方法 | 描述 |
| coroutine.create(f) | 创建一个新的协同程序,接受一个函数f作为参数,并返回一个协同程序对象。 |
| coroutine.resume(co [, val1, ...]) | 启动或再次启动一个协同程序的执行,将其状态由挂起改为运行。传递给coroutine.resume的参数会被传递到协同程序中。 |
| coroutine.yield(...) | 挂起当前的协同程序,可以返回一个或多个值给调用者。 |
| coroutine.status(co) | 查看协同程序的状态,可能的状态有:dead(已结束)、suspended(挂起)、running(运行中)。 |
| coroutine.wrap(f) | 创建一个协同程序并返回一个函数,调用这个函数会进入协同程序。这种方式使得协同程序看起来更像一个普通的函数,便于调用。 |
| coroutine.running() | 函数用于返回当前正在运行的协同程序。如果当前没有协程在运行(即在主线程中调用),则返回nil。这个函数主要是用于在复杂的协同程序嵌套或者交互场景中,确定当前是哪个协同程序在实际执行。 |

1.3 协同程序的主要特性

协同程序的主要特性总结为以下几点:

  • 非抢占式:协同程序之间不是由操作系统调度,而是由程序员控制何时挂起或恢复。
  • 状态管理:协同程序可以处于三种状态之一:suspended(挂起)、running(运行中)、dead(已结束)。当一个协同程序创建时,它的状态是suspended;调用resume启动后变为running;如果遇到yield或者完成所有操作,则转为suspended或dead状态。
  • 数据交换:可以通过resume和yield之间的参数传递来实现在协同程序内外的数据交换。resume可以向协同程序传递参数,而 yield 则可以返回值给外部。
  • 错误处理:当协同程序内部发生错误时,resume函数会接收到错误信息而不是抛出异常,这使得开发者可以在主程序中安全地处理这些错误。

2.Lua协同程序案例

以下是一个简单的Lua协同程序示例,展示了如何创建、启动、挂起和恢复协同程序。

Lua 复制代码
-- coroutine_test.lua文件
-- 创建一个新的协同程序
local function simpleCoroutine()
    print("协同程序开始")
    for i = 1, 3 do
        print("协同程序运行中,i =", i)
        -- 暂停协同程序,并返回给主程序
        coroutine.yield(i)
    end
    print("协同程序结束")
end

-- 创建协同程序并获取协同程序对象
local co = coroutine.create(simpleCoroutine)

-- 主程序通过resume来启动或继续协同程序
print("主程序:开始")

while coroutine.status(co) ~= "dead" do
    local status, value = coroutine.resume(co)
    if not status then
        print("协同程序出错:", value)
        break
    end
    print("主程序收到值:", value)
end

print("主程序:结束")

当你运行这段代码时,输出将会如下所示。

主程序:开始
协同程序开始
协同程序运行中,i =	1
主程序收到值:	1
协同程序运行中,i =	2
主程序收到值:	2
协同程序运行中,i =	3
主程序收到值:	3
协同程序结束
主程序收到值:	nil
主程序:结束

在这个例子中,simpleCoroutine是一个协同程序函数,它在执行过程中会调用coroutine.yield来暂停自己,并将控制权交还给主程序。主程序则使用coroutine.resume来启动或继续协同程序,并接收由yield返回的值。当协同程序完成它的所有操作后,它的状态会变成"dead",这时主程序就知道协同程序已经完成了。

resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。

3.生产者消费者模型

下面这个示例将展示如何使用协同程序来实现一个简单的生产者-消费者模型,其中生产者生成一系列数字,而消费者则处理这些数字。

Lua 复制代码
-- producer_consumer_test.lua文件
-- 生产者协同程序
local function producer()
    for i = 1, 5 do  -- 生产5个数字
        print("生产者: 生产数字", i)
        coroutine.yield(i)  -- 将数字发送给消费者,并挂起协同程序
    end
    print("生产者: 完成生产")
end

-- 消费者函数
local function consumer()
    local producer_co = coroutine.create(producer)  -- 创建生产者协同程序
    local value

    repeat
        -- 恢复生产者协同程序,并接收其生产的数字
        success, value = coroutine.resume(producer_co)
        if success then
            print("消费者: 接收到数字", value)
            -- 模拟处理数字(例如,简单地打印出来)
        else
            print("消费者: 生产者遇到错误", value)
            break
        end
    until not success or coroutine.status(producer_co) == "dead"

    print("消费者: 完成消费")
end

-- 主程序
print("主程序: 开始")
consumer()  -- 调用消费者函数
print("主程序: 结束")

执行以上脚本代码,程序输出结果如下。

主程序: 开始
生产者: 生产数字	1
消费者: 接收到数字	1
生产者: 生产数字	2
消费者: 接收到数字	2
生产者: 生产数字	3
消费者: 接收到数字	3
生产者: 生产数字	4
消费者: 接收到数字	4
生产者: 生产数字	5
消费者: 接收到数字	5
生产者: 完成生产
消费者: 接收到数字	nil
消费者: 完成消费
主程序: 结束

4.总结

本文主要介绍了 Lua 协同程序相关知识。首先概述了 Lua 协同程序,它是单线程环境下的轻量级并发机制,与传统多线程不同,是非抢占式的,由程序控制调度。其主要函数包括 create、resume、yield 等,协同程序有挂起、运行、结束三种状态,可进行数据交换与错误处理。

接着给出了简单示例,在 coroutine_test.lua 中,创建协同程序并通过 resume 和 yield 实现主程序与协同程序的交互,展示了其运行、暂停及数据传递过程。最后用 producer_consumer_test.lua 展示了生产者 - 消费者模型,生产者生成数字并通过 yield 传递给消费者,消费者通过 resume 接收处理,体现了协同程序在实际场景中的应用,展示了其在多任务处理方面的优势与灵活性。

相关推荐
重生之绝世牛码1 小时前
Java设计模式 —— 【行为型模式】命令模式(Command Pattern) 详解
java·大数据·开发语言·设计模式·命令模式·设计原则
晚风_END2 小时前
node.js|浏览器插件|Open-Multiple-URLs的部署和使用,实现一键打开多个URL的强大工具
服务器·开发语言·数据库·node.js·dubbo
java排坑日记3 小时前
poi-tl+kkviewfile实现生成pdf业务报告
java·pdf·word
V+zmm101344 小时前
校园约拍微信小程序设计与实现ssm+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
_周游4 小时前
【C语言】_指针与数组
c语言·开发语言
猿来入此小猿4 小时前
基于SpringBoot小说平台系统功能实现四
java·spring boot·毕业设计·毕业源码·在线小说阅读·在线小说平台·免费学习:猿来入此
SyntaxSage5 小时前
Scala语言的数据库交互
开发语言·后端·golang
疯狂小料5 小时前
Python3刷算法来呀,贪心系列题单
开发语言·python·算法
Cosmoshhhyyy5 小时前
LeetCode:2274. 不含特殊楼层的最大连续楼层数(排序 Java)
java·算法·leetcode
码力全開5 小时前
C 语言奇幻之旅 - 第14篇:C 语言高级主题
服务器·c语言·开发语言·人工智能·算法