Lua 协程

news2024/11/27 0:22:41

一、协程

Lua 中使用半协程的方式进行组织代码。

和线程的最大区别在于,一个多线程程序可以并行运行多个线程,而协程却需要彼此协作运行,即任意指定时刻只能一个协程运行,且只有当正在运行的协程显式地要求被挂起时,该协程才会被暂停。

二、协程的创建和使用

Lua 的协程创建非常简单,只需要通过 coroutine.create(f) 函数即可创建,但是创建完的协程并不会自动运行,还需要使用 coroutine.resume(co, val1, ...) 函数进行运行。

1、coroutine.create(f)

创建一个新的协程,主体为 f 函数。

参数:

  • f:是一个 Lua 函数,即协程运行的代码。

返回值:

返回新创建的协程,是一个类型为 thread 的对象

举个例子

local co = coroutine.create(function()
    print("Hello, jiang pengyong.")
end)
print("type(coroutine)", type(co))      --> type(coroutine)	thread

2、coroutine.resume(co, val1, …)

开始或继续协程 co 的执行。

当对协程 co 第一次使用 resume 启动协程时,会开始运行它的主体,会将 (val1, …) 作为参数传递给主体函数。

如果协程 co 已经挂起(通过 yield 进行挂起,下面会分享),则 resume 会重新启动它(从挂起点开始运行),并将 (val1, …) 作为 yield 的参数传递。

参数:

  • co:要被启动或重新启动的协程
  • val1,…:“作为启动的参数传递给协程” 或 “作为 yield 的接受参数”。

返回值:

  • 如果协程运行没有任何错误,则会返回 true 加上 “yield 函数抛出的参数值” 或是 “当协程终止时协程主体函数返回的值”。
  • 如果有任何错误,则返回 false 加上错误信息。

举个例子

local co = coroutine.create(function()
    print("Hello, jiang pengyong.")
end)
coroutine.resume(co)
--> Hello, jiang pengyong.

三、协程的状态

协程总共有四种状态:挂起(suspended)、运行(running)、正常(normal)、死亡(dead)。

可以通过 coroutine.status(co) 进行查看协程的状态。

1、coroutine.status(co)

获取协程 co 的状态,返回值以字符串表示:

  • running:协程正在运行
  • suspended:协程在调用 yield 时被挂起,或者还没有开始运行
  • normal:协程处于活动状态但未运行,即它已启动另一个协程(在该协程内调用了 resume 启动另一个协程)
  • dead:协程已完成其主体功能,或者因错误停止

下面一一阐述各种状态:

2、挂起(suspended)

处于这一状态的两种情况:

  • 第一种情况:协程刚被创建时(协程不会自动运行),该协程就处于这个状态。
  • 第二种情况:协程内部调用了 yield 方法,将自身挂起,此时也处于这个状态。

刚创建的协程

local co = coroutine.create(function()
    print("Hello, jiang pengyong.")
end)
print("status(coroutine)", coroutine.status(co))    --> status(coroutine)	suspended

使用 yield 进行挂起

local co = coroutine.create(function()
    coroutine.yield()
end)
coroutine.resume(co)
print(coroutine.status(co))     --> suspended

3、运行(running)

当一个协程被运行起来后,他的状态就为运行状态了

co = coroutine.create(function()
    print(coroutine.status(co))         --> running
    print("Hello, jiang pengyong.")     --> Hello, jiang pengyong.
end)
coroutine.resume(co)

4、正常(normal)

当一个协程 A 内部启动了另一个协程 B ,此时 A 处于正常状态, B 处于运行状态

local B
local A = coroutine.create(function()
    print("run A")              --> run A
    coroutine.resume(B)
    print("end A")              --> end A
end)
B = coroutine.create(function()
    print("start B")            --> start B
    print("A status", coroutine.status(A))      --> A status	normal
    print("B status", coroutine.status(B))      --> B status	running
    print("end B")              --> end B
end)
coroutine.resume(A)

5、死亡(dead)

当协程执行完了函数,就是这状态

co = coroutine.create(function()
    print(coroutine.status(co))                 --> running
    print("Hello, jiang pengyong.")             --> Hello, jiang pengyong.
end)
coroutine.resume(co)                            
print("status(coroutine)", coroutine.status(co))    --> status(coroutine)	dead

四、coroutine.running()

可以通过 coroutine.running() 进行获取当前的协程

返回值:

有返回两个值:

  • 第一个是正在运行的协程。
  • 第二个是一个布尔值,表示正在运行的协程是否为主协程,为主协程时为真。
print("coroutine.running() 主协程", coroutine.running())        --> coroutine.running() 主协程	thread: 0x7f8f1d808e08	true
local co = coroutine.create(function()
    print("coroutine.running()", coroutine.running())           --> coroutine.running()	thread: 0x6000020e4278	false
end)
print(coroutine.resume(co))

五、协程的传值

协程的传值由两个函数进行 resumeyieldresume 已经在前面介绍了,这里介绍下 yield

1、coroutine.yield(…)

挂起当前协程,yield 会将参数(…)都作为结果传递给 resume

2、使用

local co = coroutine.create(function(x)
    print("接收第一次参数:", x)
    a, b, c, d, e = coroutine.yield("第一次返回值")
    print("接收第二次参数:", a, b, c, d, e)
    return "第二次返回值"
end)
print(coroutine.resume(co, "hi"))
print(coroutine.resume(co, 4, 5, "江澎涌"))

--> 接收第一次参数:	hi
--> true	第一次返回值
--> 接收第二次参数:	4	5	江澎涌	nil	nil
--> true	第二次返回值

当协程还没有启动的时候,则会将 resume 第二个开始携带的参数当作协程的方法参数。

当协程内部调用 yield 时,协程会挂起。在挂起协程的同时,将 yield 的参数作为返回值给到外部启动该协程的 resume

当外部再次启动协程时,同样会将 resume 第二个开始携带的参数当作入参给到 yield 函数,可以理解为 yield 的返回值。

最后当协程方法运行完后,如果有 return 将值返回,则会被用作 resume 的返回值。

3、一图胜千言

可以通过下图理解 createresumeyield 即协程返回值间的关系

六、对协程异常的捕获

resume 方法是运行在保护模式中的,所以一旦协程内部发生错误,则会抛给 resume 方法,resume 方法会得到两个返回值,一个是 false,一个是错误原因。

local co = coroutine.create(function()
    error("协程内部错误")
    coroutine.yield()
end)
print(coroutine.resume(co))     --> false	...ong/Desktop/study/lua_study_2022/17 协程/coroutine.lua:50: 协程内部错误
print(coroutine.status(co))     --> dead

七、coroutine.wrap(f)

wrap 和 create 有些类似,只是 wrap 返回的不是协程,而是一个函数,每次调用这个函数,则会相当于这个协程被 resume 了一次。还有当协程方法内部发生异常,则会导致抛出异常,不会被保护模式所获取。

对于传值方面,和 yield-resume 是一样的。

local co = coroutine.wrap(function(name)
    print(string.format("Hello, %s.", name))    --> Hello, jiang pengyong.
    local age = coroutine.yield("Hi.")          --> Hi.
    print(age)                                  --> 29
    return "Bye."
end)
print(co("jiang pengyong"))     --> Hi.
print(co("29"))                 --> Bye.

1、coroutine.wrap(f) 和 coroutine.create(f)

coroutine.wrap(f) 使用简单,只是一个唤醒函数。

coroutine.create(f) 可以更加灵活的控制,例如可以获取协程的状态,捕获异常。

八、coroutine.isyieldable()

检测正在运行的协程是否可以挂起,如果可以挂起则为 true。

如果运行中的协程不是主线程并且不在不能挂起的 C 函数中,则他是可挂起的。

local co = coroutine.create(function()
    print(coroutine.isyieldable())      --> true
    print("Hello, jiang pengyong.")     --> Hello, jiang pengyong.
end)
coroutine.resume(co)                    
print(coroutine.isyieldable())          --> false

九、写在最后

Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)

如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀

公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。

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

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

相关文章

Net6 用imagesharp 实现跨平台图片处理并存入oss

项目要求:生成电子证书 一、模板文件在OSS中,直接加载 二、向模板文件添加二维码 三、向模板文件添加多行文字 四、生成二维码,存入本地, 五、向模板文件添加二维码 代码实现步骤 一、建立.net 6 API项目,安装N…

SpringBoot-黑马程序员-学习笔记(五)

74.自定义bean属性绑定以及第三方bean属性绑定 自定义bean属性绑定 1.自定义一个bean Data Component public class ServerConfig {private String ipAddress;private int port;private long timeout; } 2.在yml配置文件中中定义一组值 3.在bean中进行属性绑定 加上这个注…

【Linux】:Linux环境与版本

以下哪个命令输出Linux内核的版本信息 A.uname -r B.vmstat C.sar D.stat uname -r 查看linux内核版本信息 vmstat 报告关于内核线程、虚拟内存、磁盘、陷阱和 CPU 活动的统计信息 sar 主要帮助我们掌握系统资源的使用情况,特别是内存和CPU的使用情况 stat 用于显示…

【从零开始学习Redis | 第二篇】Redis中的数据类型和相关命令

前言: Redis是一种快速、高效的开源内存数据库,被广泛用于构建各种类型的应用程序。其被设计成支持多种数据类型,这使得Redis在处理各种场景的数据存储和操作中非常灵活。Redis的数据类型提供了对不同数据结构的直接支持,包括字符…

并查集维护集合 ac240食物链

题目&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; const int N50010; int p[N],d[N]; int n,m;int find(int x){if(p[x]!x){ int ufind(p[x]);d[x] d[p[x]];p[x]u;}return p[x]; }int main(){scanf("%d%d",&n,&m);fo…

Java设计模式之六大设计原则

为什么要学习设计模式&#xff1f; 要知道设计模式就是软件工程的方法经验的总结&#xff0c;也是可以认为是过去一段时间软件工程的一个最佳实践&#xff0c;要理解&#xff0c;不要死记硬背。掌握这些方法后&#xff0c;可以让你的程序获得以下好处&#xff1a; 代码重用性…

SpringCloud-Hystrix

一、介绍 &#xff08;1&#xff09;避免单个服务出现故障导致整个应用崩溃。 &#xff08;2&#xff09;服务降级&#xff1a;服务超时、服务异常、服务宕机时&#xff0c;执行定义好的方法。&#xff08;做别的&#xff09; &#xff08;3&#xff09;服务熔断&#xff1a;达…

学习记忆——题型篇——写作——记忆宫殿法

1&#xff0e;什么是数字记忆法? 答&#xff1a; 数字记忆就是把每一个数字转换成图片编码后再进行联想速记。 2&#xff0e;数字记忆法的用途有哪些&#xff1f; 答&#xff1a; 可以记忆学科知识&#xff0c;如地理、历史等所有学科或考试中的数据信息&#xff1b;可以速记生…

给ChuanhuChatGPT 配上讯飞星火spark大模型V2.0(一)

ChuanhuChatGPT 拥有多端、比较好看的Gradio界面&#xff0c;开发比较完整&#xff1b; 刚好讯飞星火非常大气&#xff0c;免费可以领取大概20w&#xff08;&#xff01;&#xff01;&#xff01;&#xff09;的token&#xff0c;这波必须不亏&#xff0c;整上。 重要参考&am…

中断机制-中断协商机制、中断方法

4.1 线程中断机制 4.1.1 从阿里蚂蚁金服面试题讲起 Java.lang.Thread下的三个方法: 4.1.2 什么是中断机制 首先&#xff0c;一个线程不应该由其他线程来强制中断或停止&#xff0c;而是应该由线程自己自行停止&#xff0c;自己来决定自己的命运&#xff0c;所以&#xff0c;…

创意无限,动画随心——Adobe Animate 2024正式发布!

Adobe Animate 2024是一款全新的多平台动画和互动设计工具&#xff0c;它为用户提供了强大的工具和功能&#xff0c;以创造出各种类型的动画作品。无论是短片、广告、游戏还是交互式应用程序&#xff0c;Animate都能够满足您的需求。 Animate 2024的主要特点包括&#xff1a; …

mysql面试题48:MySQL中 Innodb的事务与日志的实现方式

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官: Innodb的事务与日志的实现方式 以下是InnoDB事务和日志的实现方式的详细说明: 事务日志(Transaction Log): InnoDB使用事务日志来保证事务的…

windows TBB的使用

windows TBB的使用 1. Install with GUI 1. Install with GUI To install oneTBB using GUI, complete the following steps: Go to the Download page.Select the preferred installer Online installer has a smaller file size but requires a permanent Internet connec…

计算机网络 | 网络层

计算机网络 | 网络层 计算机网络 | 网络层功能概述SDN&#xff08;Software-Defined Networking&#xff09;路由算法与路由协议IPv4IPv4 分组IPv4 分组的格式IPv4 数据报分片 参考视频&#xff1a;王道计算机考研 计算机网络 参考书&#xff1a;《2022年计算机网络考研复习指…

前端基础一:用Formdata对象来上传图片的原因

最近有人问&#xff1a;你是否能用json来传图片&#xff0c;其实应该这么理解就对了。 一、上传的数据体格式Content-Type 1.application/x-www-form-urlencoded 2.application/json 3.multipart/form-data 以上三种类型旨在告诉服务器需要接收的数据类型同事要…

企业级CI/CD 持续集成/交付/发布

jenkins 安装与使用 nmcli g hostname jenkins 加载缓存 yum makecache fast 上传jdk11、jdk8 获取、上传war包 1、jenkins.io/download 2.4.27 2、老师发的 上传 maven 上传tomcat软件包 &#xff08;apache.org-tomcat8-下载&#xff09; 注意8009端口 /usr... vi /etc/pro…

「蓝桥·算法双周赛」第一场公开赛

三带一【算法赛】 - 蓝桥云课 (lanqiao.cn) 给定四个字符&#xff0c;判断是否其中有三个相同&#xff0c;另一个与他们不同 #include <bits/stdc.h> void solve() {std::string s;std::cin>>s;char as[0],bs[1],cs[2],ds[3];if(ab&&ac&&a!d) std:…

Puppeteer监听网络请求、爬取网页图片(二)

Puppeteer监听网络请求、爬取网页图片&#xff08;二&#xff09; Puppeteer监听网络请求、爬取网页图片&#xff08;二&#xff09;一、爬取需求二、实现讲解三、效果查看 一、爬取需求 首先打开浏览器&#xff0c;打开指定网站监听网站发出的所有请求&#xff0c;记录请求&a…

【数据结构】线性表与顺序表

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 线性表与顺序表 1. 线性表2. 顺序表2.1 …

【Java 进阶篇】JavaScript 正则表达式(RegExp)详解

JavaScript 正则表达式&#xff0c;通常简写为 RegExp&#xff0c;是一种强大的文本匹配工具&#xff0c;它允许你通过一种灵活的语法来查找和替换字符串中的文本。正则表达式在编程中用途广泛&#xff0c;不仅限于 JavaScript&#xff0c;在许多编程语言中也都有类似的实现。 …