Lua协同程序(线程)

news2025/1/12 2:27:03

1、 Lua 的协同程序(coroutine)简单介绍

        Lua 的协同程序(coroutine)是一种轻量级的线程,允许你在多个任务之间进行协作式多任务处理。与操作系统线程不同,协同程序是由程序员显式控制的,不会自动切换,而是通过 yield 和 resume 来手动切换执行权。

        协同程序的核心思想是协作式多任务,即一个任务主动让出执行权,另一个任务才能继续执行。这种机制非常适合需要分步执行的任务,例如状态机、迭代器、异步任务等。

  1. 创建协同程序:

    • 使用 coroutine.create(f) 创建一个协同程序,其中 f 是一个函数。

    • 返回的是一个协同程序对象(类型为 thread)。如果看过我之前的文章,其实这个有点像pthread_create,即创建一个线程,但是两个是不一样的东西

  2. 启动或恢复协同程序:

    • 使用 coroutine.resume(co, ...) 启动或恢复一个协同程序。

    • 可以传递参数给协同程序。

  3. 让出执行权:

    • 在协同程序内部,使用 coroutine.yield(...) 让出执行权,并返回一些值给调用者。

  4. 检查协同程序状态:

    • 使用 coroutine.status(co) 检查协同程序的状态:

      • "running":正在运行。

      • "suspended":暂停(等待恢复)。

      • "dead":已经结束。

  5. 获取当前运行的协同程序:

    • 使用 coroutine.running() 获取当前正在运行的协同程序。

 2、lua的协同程序和pthread的区别

    pthread_create 和 Lua 的协同程序(coroutine)是两种完全不同的并发机制,它们的实现方式、使用场景和行为都有显著区别。

1. pthread_create

pthread_create 是 POSIX 线程(pthread)库中的一个函数,用于创建操作系统级别的线程。

  • 操作系统线程:pthread_create 创建的是真正的操作系统线程,由操作系统调度。线程是抢占式的,操作系统会在任意时刻切换线程的执行。

  • 并发性:多个线程可以并行运行(如果有多核 CPU)。线程之间是真正并发的。

  • 资源开销:线程的创建和切换需要较大的资源开销(内存、上下文切换等)。线程数量受操作系统限制。

  • 同步和通信:线程之间需要通过锁(如 pthread_mutex)、条件变量( pthread_cond)等机制进行同步和通信。容易出现竞态条件(race condition)和死锁(deadlock)。

  • 使用场景:适合需要真正并行执行的场景,例如 CPU 密集型任务或需要利用多核性能的任务。

2. Lua 协同程序(coroutine)

Lua 的协同程序是一种用户态的轻量级线程:

  • 用户态线程:

    • 协同程序是由 Lua 虚拟机管理的,不依赖操作系统线程。

    • 协同程序是协作式的,需要显式调用 yield 和 resume 来切换任务。

  • 并发性:

    • 协同程序是单线程的,同一时刻只有一个协同程序在运行。

    • 无法利用多核 CPU 实现真正的并行。

  • 资源开销:

    • 协同程序的创建和切换开销非常小,适合高并发场景。

    • 可以创建成千上万个协同程序。

  • 同步和通信:

    • 协同程序之间不需要锁或条件变量,因为它们是协作式的。

    • 数据共享更简单,不容易出现竞态条件。

  • 使用场景:

    • 适合 I/O 密集型任务、状态机、迭代器等需要分步执行的场景。

    • 不适合 CPU 密集型任务。

3、协同程序的相关函数解析

1. coroutine.create(f)
  • 功能: 创建一个新的协同程序。

  • 参数:f: 一个函数,作为协同程序的主体。

  • 返回值:返回一个协同程序对象(类型为 thread)。

  • 2. coroutine.resume(co, ...)
  • 功能: 启动或恢复一个协同程序的执行。

  • 参数:co: 协同程序对象(由 coroutine.create 创建)。...: 可选参数,传递给协同程序的参数。

  • 返回值:第一个返回值是一个布尔值,表示协同程序是否成功执行。后续返回值是协同程序通过 coroutine.yield 或 return 返回的值。

  • 3.coroutine.yield(...)
  • 功能: 暂停当前协同程序的执行,并返回一些值给调用者。

  • 参数:...: 可选参数,作为 yield 的返回值。

  • 返回值:无返回值(因为 yield 会暂停执行)。

  • 注意:只能在协同程序内部调用。

4.coroutine.status(co)
  • 功能: 获取协同程序的当前状态。

  • 参数:co: 协同程序对象。

  • 返回值:返回一个字符串,表示协同程序的状态:

    • "dead": 已经结束。

    • "suspended": 暂停(等待恢复)。

    • "running": 正在运行。

 5.coroutine.running()
  • 功能: 获取当前正在运行的协同程序。

  • 参数:无。

  • 返回值:返回当前正在运行的协同程序对象。如果当前不在协同程序中,返回 nil

6.coroutine.wrap(f)
  • 功能: 创建一个新的协同程序,并返回一个函数,调用该函数会恢复协同程序的执行。

  • 参数:f: 一个函数,作为协同程序的主体。

  • 返回值:返回一个函数,调用该函数相当于调用 coroutine.resume

  • 注意:

    • 与 coroutine.create 不同,coroutine.wrap 返回的函数会直接返回 yield 的值,而不是布尔状态。

 看下面这个代码,会输出什么,如果按照c语言当中的语法规则,这里可能会直接输出开始执行,然后停止,但是这是在lua语法规则当中,所以对于这里来说,会输出nil,nil,也就是两个空,那么为什么会这样呢,Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。这里我们只是定义了一个函数,函数 foo 中的输出没有执行,是因为 Lua 的协同程序是惰性执行的。也就是说,协同程序不会自动执行,必须通过 coroutine.resume 显式地启动或恢复它。:

function foo()
        print("开始执行")
        local value=coroutine.yield("暂停执行")
        print("协同恢复执行")
        print("结束执行")
end
--local co=coroutine.create(foo)

--local status,result=coroutine.resume(co)
print(result)

--status,result=coroutine.resume(co,42)
print(result)

 如果将注释去掉,这样之后就能够执行代码了,这时候协同程序被创建

function foo()
        print("开始执行")
        local value=coroutine.yield("暂停执行")
        print("协同恢复执行,传入"..tostring(value))
        print("结束执行")
end
local co=coroutine.create(foo)

local status,result=coroutine.resume(co)
print(status,result)

status,result=coroutine.resume(co,42)
print(status,result)

local status, result = coroutine.resume(co)
print(status,result)  -- 输出: true,暂停执行

-- 恢复协同程序,并传入一个值
status, result = coroutine.resume(co, 42)

这三行代码如何理解呢?看resume这个函数,这个函数被调用了两次,第一次用来启动协同程序,而且resume这个代码来说有两个返回值,第一个返回值给到status,如果执行成功,返回true,第二个返回值给到result,返回的是暂停执行。那么问题来了,为什么返回值是yield函数的参数呢,这是因为,协同程序是一个特殊的线程,也就是单线程,一次只能运行一个线程,不是并行执行,如果是操作系统里面的线程执行,会跳转到线程当中执行,并且当前代码也会继续执行,但是在lua当中,线程执行只能有一个,而这里会在foo函数当中继续执行,直到遇到暂停函数,这时候会暂停线程,回来执行下面的代码,也就是print函数,这时候打印出来的值就是yield函数的参数,第二次使用resume是用来恢复协同程序,因为上面的协同程序被暂停了,这时候会再次恢复协同程序,但是不会在刚才已经执行过的代码继续执行,而是执行已经执行过的下一行代码。那么如果这时候再有一个resume函数呢?看下面的代码:

function foo()
        print("开始执行")
        local value=coroutine.yield("暂停执行")
        print("协同恢复执行,传入"..tostring(value))
        print("结束执行")
end
local co=coroutine.create(foo)

local status,result=coroutine.resume(co)
print(status,result)

status,result=coroutine.resume(co,42)
print(status,result)
status,result=coroutine.resume(co,42)
print(result)

 这段代码当中拥有三个resume函数,我们知道,第一和第二个是用来启动和恢复协同线程的,这时候第三个会受到影响吗?

直接看输出结果:

开始执行
true	暂停执行
协同恢复执行,传入42
结束执行
true	nil
cannot resume dead coroutine

这时候的输出结果出现了一句cannot resume dead corutine 也就是说,cannot resume dead coroutine 是 Lua 中的一个错误提示,表示你尝试恢复一个已经**死亡(dead)**的协同程序。协同程序一旦执行完毕(即函数返回或执行到末尾),就会进入 dead 状态,此时再调用 coroutine.resume 就会触发这个错误。这是一个错误提示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275252.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

USRP X310 Windows 烧录镜像

说明 USRP-X 系列设备包含两个用于两个以太网通道的 SFP 端口。由于 SFP 端口支持 1 千兆 (SFP) 和 10 千兆 (SFP) 收发器,因此 UHD 附带了多个 FPGA 图像,以确定上述接口的行为。 注意:Aurora 图像需要从 FPGA 源代码手动构建。 FPGA 图像…

新型物联网智能断路器功能参数介绍

安科瑞刘鸿鹏 摘要 智能断路器作为现代配电系统的重要组成部分,以其实时监测、多重保护和远程操控的智能化功能,显著提升了电力系统的运行效率和安全性。本文以ASCB1系列智能断路器为例,探讨其技术特点和在工业、商业及民用建筑中的应用价…

119.使用AI Agent解决问题:Jenkins build Pipeline时,提示npm ERR! errno FETCH_ERROR

目录 1.Jenkins Build时的错误 2.百度文心快码AI智能体帮我解决 提问1:jenkins中如何配置npm的源 提问2:jenkins pipeline 类型为pipeline script from SCM时,如何配置npm源 3.最终解决方法-Jenkinsfile的修改 4.感触 1.Jenkins Build时…

pytest+allure 入门

使用allure如何生成自动化测试报​​​​​​告 ?一文详解allure的使用 。_allure测试报告-CSDN博客 例子: import allure import pytest import osallure.epic("闹钟") allure.feature("闹钟增删") class TestSchedule():def setu…

【FPGA】时序约束与分析

设计约束 设计约束所处环节: 约束输入 分析实现结果 设计优化 设计约束分类: 物理约束:I/O接口约束(例如引脚分配、电平标准设定等物理属性的约束)、布局约束、布线约束以及配置约束 时序约束:设计FP…

【Vim Masterclass 笔记09】S06L22:Vim 核心操作训练之 —— 文本的搜索、查找与替换操作(第一部分)

文章目录 S06L22 Search, Find, and Replace - Part One1 从光标位置起,正向定位到当前行的首个字符 b2 从光标位置起,反向查找某个字符3 重复上一次字符查找操作4 定位到目标字符的前一个字符5 单字符查找与 Vim 命令的组合6 跨行查找某字符串7 Vim 的增…

win32汇编环境,窗口程序中对按钮控件常用操作的示例

;运行效果 ;win32汇编环境,窗口程序中对按钮控件常用操作的示例 ;常用的操作,例如创建按钮控件,使其无效,改变文本,得到文本等。 ;将代码复制进radasm软件里,直接就可以编译运行。重点部分加备注。 ;>&g…

继承(7)

大家好,今天我们继续来学习一下继承的知识,这方面需要大家勤动脑才能理解,那么我们来看。 1.9 protected关键字 在类和对象章节中,为了实现封装特性,java中引入访向限定符,主要限定:类或者类中成员能否在类外和其他包中被访问. …

基于RK3568/RK3588大车360度环视影像主动安全行车辅助系统解决方案,支持ADAS/DMS

产品设计初衷 HS-P2-2D是一款针对大车盲区开发的360度全景影像 安全行车辅助系统,通过车身四周安装的超广角像机,经算法合成全景鸟瞰图,通过鸟瞰图,司机非常清楚的看清楚车辆四周情况,大大降低盲区引发的交通事故。 产…

NVIDIA发布GeForce RTX 50 系列,售价549美元起

2025 CES消费电子展(1月7日至10日,美国拉斯维加斯)正式开幕。北京时间1月7日 (星期二)上午10:30,NVIDIA举办主题演讲,CEO黄仁勋担任主讲。正式发布了全新的RTX 50系列显卡!一月下旬上市。同时公布了各版本的…

后端:Spring(IOC、AOP)

文章目录 1. Spring2. IOC 控制反转2-1. 通过配置文件定义Bean2-1-1. 通过set方法来注入Bean2-1-2. 通过构造方法来注入Bean2-1-3. 自动装配2-1-4. 集合注入2-1-5. 数据源对象管理(第三方Bean)2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)2-1-7. 加载容器…

基于EasyExcel实现通用版一对一、一对多、多层嵌套结构数据导出并支持自动合并单元格

接口功能 通用 支持一对一数据结构导出 支持一对多数据结构导出 支持多层嵌套数据结构导出 支持单元格自动合并 原文来自:https://blog.csdn.net/qq_40980205/article/details/136564176 新增及修复 基于我自己的使用场景,新增并能修复一下功能&#x…

【数据库】一、数据库系统概述

文章目录 一、数据库系统概述1 基本概念2 现实世界的信息化过程3 数据库系统内部体系结构4 数据库系统外部体系结构5 数据管理方式 一、数据库系统概述 1 基本概念 数据:描述事物的符号记录 数据库(DB):长期存储在计算机内的、…

网络安全建设方案,信息安全风险评估报告,信息安全检测文档(Word原件完整版)

一、概述 1.1工作方法 1.2评估依据 1.3评估范围 1.4评估方法 1.5基本信息 二、资产分析 2.1 信息资产识别概述 2.2 信息资产识别 三、评估说明 3.1无线网络安全检查项目评估 3.2无线网络与系统安全评估 3.3 ip管理与补丁管理 3.4防火墙 四、威胁细…

数据分析工作流

数据分析工作流 1.流程 数据产生阶段 业务系统生成数据:在各种业务场景下,如用户在电商平台上进行购物(产生订单信息、浏览记录等)、在金融系统中进行交易(产生交易流水、账户余额变动等)或者在企业内部的…

【Go】:图片上添加水印的全面指南——从基础到高级特性

前言 在数字内容日益重要的今天,保护版权和标识来源变得关键。为图片添加水印有助于声明所有权、提升品牌认知度,并防止未经授权的使用。本文将介绍如何用Go语言实现图片水印,包括静态图片和带旋转、倾斜效果的文字水印,帮助您有…

PyQt5 UI混合开发,控件的提升

PromoteLabelTest.py 提升的类 import sys from PyQt5.QtWidgets import QApplication, QWidget,QVBoxLayout,QTextEdit,QPushButton,QHBoxLayout,QFileDialog,QLabelclass PromoteLabel(QLabel):def __init__(self,parent None):super().__init__(parent)self.setText("…

CI/CD 流水线

CI/CD 流水线 CI 与 CD 的边界CI 持续集成CD(持续交付/持续部署)自动化流程示例: Jenkins 引入到 CI/CD 流程在本地或服务器上安装 Jenkins。配置 Jenkins 环境流程设计CI 阶段:Jenkins 流水线实现CD 阶段:Jenkins 流水…

ROS核心概念解析:从Node到Master,再到roslaunch的全面指南

Node 在ROS中,最小的进程单元就是节点(node)。一个软件包里可以有多个可执行文件,可执行文件在运行之后就成了一个进程(process),这个进程在ROS中就叫做节点。 从程序角度来说,node就是一个可执行文件&…

深入Android架构(从线程到AIDL)_22 IPC的Proxy-Stub设计模式04

目录 5、 谁来写Proxy及Stub类呢? 如何考虑人的分工 IA接口知识取得的难题 在编程上,有什么技术可以实现这个方法? 范例 5、 谁来写Proxy及Stub类呢? -- 强龙提供AIDL工具,给地头蛇产出Proxy和Stub类 如何考虑人的分工 由框架开发者…