lua 协同程序(coroutine)

2023-11-05

本文主要是根据菜鸟教程的lua协同程序(coroutine)优化出来的文章。

对于网上的lua协同程序的文章,觉得菜鸟教程里的lua协同程序(coroutine)比较完善,但还是有一些地方没有说清楚,对于像我这样的新手学习起来,还是不能完全解惑,故在前人的文章上再作详细的注释。

以下红色字体为修改部分。

什么是协同(coroutine)?

Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。

协同是非常强大的功能,但是用起来也很复杂。

线程和协同程序区别

线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。

在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起(调用yield)的时候才会被挂起。

协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。

基本语法

方法 描述
coroutine.create() 创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用
coroutine.resume() 重启coroutine,和create配合使用

调用create之后,要调用resume,协程才会开始。

第一次之后的所有resume,程序都会回到上一次yield(即完成yield的赋值操作)那里,并且yield的返回值是resume的参数。

coroutine.yield()

挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果。

调用yield后,即退出协程回调,回到之前调用resume的代码位置(resume可以是在主线程调用,也可以在其他协程调用)。

调用yield或协程的回调执行完后,程序控制权会回到resume所以在的位置(即完成resume的赋值操作)。

如果协程回调没结束,则resume的返回值是yield的参数;否则,resume的返回值是回调的返回值。

coroutine.status() 查看coroutine的状态
注:coroutine的状态有三种:dead,suspend,running,具体什么时候有这样的状态请参考下面的程序
coroutine.wrap() 创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复
coroutine.running() 返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号

以下实例演示了以上各个方法的用法:

-- coroutine_test.lua 文件
co = coroutine.create(
    function(i)
        print(i);
    end
)
 
coroutine.resume(co, 1)   -- 1
print(coroutine.status(co))  -- dead
 
print("----------")
 
co = coroutine.wrap(
    function(i)
        print(i);
    end
)
 
co(1)
 
print("----------")
 
co2 = coroutine.create(
    function()
        for i=1,10 do
            print(i)
            if i == 3 then
                print(coroutine.status(co2))  --running
                print(coroutine.running()) --thread:XXXXXX
            end
            coroutine.yield()
        end
    end
)
 
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
 
print(coroutine.status(co2))   -- suspended
print(coroutine.running())
 
print("----------")

以上实例执行输出结果为:

1
dead
----------
1
----------
1
2
3
running
thread: 0x7fb801c05868
suspended
nil
----------

coroutine.running就可以看出来,coroutine在底层实现就是一个线程。

当create一个coroutine的时候就是在新线程中注册了一个事件。

当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。

接下来我们分析一个更详细的实例:

function foo (a)
    print("foo 函数输出", a)
    return coroutine.yield(2 * a) -- 返回  2*a 的值
end
 
co = coroutine.create(function (a , b)
    print("第一次协同程序执行输出", a, b) -- 1 10
    local r = foo(a + 1)
     
    print("第二次协同程序执行输出", r)
    local r, s = coroutine.yield(a + b, a - b)  -- ab的值为第一次调用协同程序时传入
     
    print("第三次协同程序执行输出", r, s)  -- r,s的值为第二次调用协同程序时传入
    return b, "结束协同程序"
end)
        
print("main", coroutine.resume(co, 1, 10)) -- true,  4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11  -9
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true  10
print("---至此,协程结束---")
print("main", coroutine.resume(co, "x", "y")) -- false  cannot resume dead coroutine
print("---分割线---")

以上实例执行输出结果为:

第一次协同程序执行输出    1    10
foo 函数输出    2
main    true    4
--分割线----
第二次协同程序执行输出    r
main    true    11    -9
---分割线---
第三次协同程序执行输出    x    y
main    true    10    结束协同程序
---至此,协程结束---
main    false    cannot resume dead coroutine
---分割线---

以上实例接下如下:

  • 调用resume,将协同程序唤醒,resume操作成功返回true,否则返回false;
  • 协同程序运行;
  • 运行到yield语句;
  • yield挂起协同程序,第一次resume返回;(注意:此处yield返回,参数是resume的参数)
  • 第二次resume,再次唤醒协同程序;(注意:此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数)
  • yield返回;
  • 协同程序继续运行;
  • 如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine

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


生产者-消费者问题

现在我就使用Lua的协同程序来完成生产者-消费者这一经典问题。

function productor()
     local i = 0
     while i < 100 do
          i = i + 1
          print("productor", i)
          coroutine.yield(i);    --将产品发送给消费者
     end
end

local newProductor = coroutine.create(productor)
local j = 0
function consumer()
     while j < 100 do
     	j = j + 1;
        local status, value = coroutine.resume(newProductor);   -- 从生产者那里得到产品
        print("consumer", status, value)
     end
end

local newConsumer = coroutine.create(consumer)

-- 启动程序
coroutine.resume(newConsumer)

以上实例执行输出结果为:

productor	1
consumer	true	1
productor	2
consumer	true	2
productor	3
consumer	true	3
productor	4
consumer	true	4
productor	5
consumer	true	5
productor	6
consumer	true	6
productor	7
consumer	true	7
productor	8
consumer	true	8
productor	9
consumer	true	9
productor	10
consumer	true	10
productor	11
consumer	true	11
productor	12
consumer	true	12
……

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

lua 协同程序(coroutine) 的相关文章

  • 覆盖 require 后调用 lua_getfield() 时崩溃

    这个问题与 Henri Menke 在这个问题中的回答相关 如何在 C 中获取预加载模块名称 https stackoverflow com questions 51510308 how to get preloaded module na
  • 迭代包括空行在内的行

    给定一个带有一些空行的多行字符串 我如何在Lua中迭代行包括空行 local s foo nbar n njim for line in magiclines s do print line and blank or line end gt
  • 如何使用循环将字符串连接成一个?

    有人可以帮我解决字符串连接问题吗 我从寄存器读取数据 它的函数 utf regAddr length 我得到带有十进制数字的表格 然后将其转换为十六进制并循环字符串 我需要将这些字符串连接成一个 Lua中没有像 这样的操作符 functio
  • 如何在上下文中运行协程?

    在关于 Context Vars 的 Python 文档中 https docs python org 3 library contextvars htmlContext run 方法被描述为能够在上下文内执行可调用的操作 以便对上下文的可
  • 十六进制常数 = 格式错误的数字?

    我有一个 Lua 脚本 我试图在其中使用十六进制数字 0x 如果我使用官方 Windows 二进制文件在控制台中运行此脚本 它可以正常工作 但是如果我在我的应用程序中运行它 简单的 dofile 我得到 malformed number n
  • 将回调地狱转换为延迟对象

    背景 所以 我有一个相当大的项目 有很多 API 函数 我正在考虑完全转向协程 但由于它们的实现方式是Callback并不是Deferred 我无法有效地使用它们 例如 我想做apiCallOne apiCallTwo and apiCal
  • Python 中基于收益的协程与带有 @asyncio.coroutine 和 @types.coroutine 装饰器的协程有何不同?

    我一直在尝试理解异步编程 尤其是 Python 中的异步编程 我知道 asyncio 是基于事件循环构建的 该事件循环安排协程的执行 但我已经阅读了几种定义协程的不同方法 并且我很困惑它们如何相互关联 I read 本文 http masn
  • Redis 排序集和解决关系

    我正在使用 Redis 排序集来存储我正在处理的项目的排名 我们没有预料到 我们想要如何处理关系 Redis 按字典顺序对具有相同分数的条目进行排序 但我们想要做的是对具有相同分数的所有条目给予相同的排名 例如在以下情况 redis 127
  • Lua-迭代嵌套表

    我已经学习 Lua 几个星期了 这一次又一次成为我的症结所在 我尝试阅读有关该主题的帖子和书籍 我使用 Lua 查询软件监控系统 Nimsoft 我的数据以表格形式返回给我 我不会发布整个输出 但这里有一个我认为可以描述结构的片段 表参考是
  • Lua :: 如何编写加载多个CPU的简单程序?

    我还无法用 Lua 编写一个可以加载多个 CPU 的程序 自从Lua通过协程支持这个概念 http www lua org pil 9 4 html 我相信这是可以实现的 我失败的原因可能是以下之一 这在Lua中是不可能的 我写不出来 an
  • 确定已编译Lua的编译器版本

    我有一些已编译的 LuaQ 我需要确定用于编译它的确切版本 有什么可能的方法吗 编译的脚本在文件开头有一个标头 4 bytes signature x1bLua 1 byte version 0x51 1 byte format 1 byt
  • 在lua中组合两个函数

    我刚开始学习lua 所以我的要求可能是不可能的 现在 我有一个接受函数的方法 function adjust focused window fn local win window focusedwindow local winframe w
  • 使用 FastCGI 运行 Lua 脚本

    我目前正在尝试找出使用 FastCGI 与 lighttpd 或 Nginx 一起运行 Lua 脚本的方法 我唯一能挖到的是WSAPI http keplerproject github com wsapi 开普勒计划的一部分 但我想知道是
  • 去掉尾随零和小数点

    使用 Lua 我将数字格式化为可变位数并去掉尾随零 小数点 例如 string format precision f value gsub 0 1 gsub 值的类型为数字 正数 负数 整数 小数 所以任务已经解决了 但出于美学 教育和性能
  • Lua 访问表的键和值

    我想在关卡编辑器中读取 Lua 文件 这样我就可以以可视化格式显示其数据供用户编辑 如果我有一个像这样的 Lua 表 properties Speed 10 TurnSpeed 5 Speed显然是关键并且10价值 我知道如果我知道像这样的
  • 在Luasocket中,在什么条件下,即使在select告诉它可以安全读取之后,accept调用也可以阻塞?

    卢阿索基特select http w3 impa br diego software luasocket socket html select函数应该告诉何时可以在不阻塞的情况下读取套接字 它显然也可以用来告诉服务器套接字何时准备好接受新连
  • 如何将依赖 ThreadLocal 的代码与 Kotlin 协程一起使用

    一些 JVM 框架使用ThreadLocal存储应用程序的调用上下文 例如SLF4j MDC https logback qos ch manual mdc html 事务管理器 安全管理器等 然而 Kotlin 协程是在不同的线程上调度的
  • LuaJ 导入 Lua 方法

    我正在使用 LuaJ 并且我有一个 lua文件充满了一堆函数 如何通过 LuaJ 导入这些函数以在 Java 中使用 一种选择是将文件编译为 Java 代码并导入它 另一种方法是使用可嵌入解释器直接从 Java 代码中调用 Lua 文件 E
  • gsub 的转义字符串

    我读了一个文件 local logfile io open log txt r data logfile read a print data output n w r 1 2 n t x re S 是的 日志文件看起来很糟糕 因为它充满了各
  • Lua 的标准(或最好支持的)大数(任意精度)库是什么?

    我正在处理大量无法四舍五入的数字 使用 Lua 的标准数学库 似乎没有方便的方法来保持精度超过某些内部限制 我还看到有几个库可以加载以处理大数字 http oss digirati com br luabignum http oss dig

随机推荐

  • 使用带域名的docker容器

    之前要研究一些新兴技术时 都是在自己电脑上安装虚拟机 使用起来有些许不变 例如想搭建一套mysql的双主半同步 并使用keepalived来保证高可用 就需要创建2台虚拟机 很是不方便 于是就寻找使用更方便的虚拟技术 docker就可以很好
  • java手工分页工具类

    手工分页 手工分页用处 当数据使用mybatisPlus分页查询后 需要再次过滤掉筛选数据时 返回的总数失效 此时可以使用手工分页 kotlin手工分页工具类 Component class ManualPageUtils 封装手动分页 p
  • java定时器实现原理_java timer(定时器)执行原理分析

    1 首先看Timer源码 public class Timer private final TaskQueue queue new TaskQueue private final TimerThread thread new TimerTh
  • 文件编译【上】

    1 程序的翻译环境和执行环境 翻译环境 在翻译环境中 源文件被翻译为可执行的机器指令 执行环境 实际执行代码的环境 2 编译与链接 组成一个程序的每个源文件通过编译过程分别转换成目标代码 object code 每个目标文件由链接器 lin
  • AR联机初探+官方项目代码解析

    学AR也有一小段时间了 今天给大家分享一下如何让两部以上的设备查看到相同的增强现实景象 在这里作者就以苹果官方的示例来进行解析 一定要把代码下载了和文章对照着看 不然会懵 官方项目代码地址 https developer apple com
  • 前后端连接超时设置

    在application properties中添加 server servlet session timeout 60 单位为秒 在前端添加 ajax timeout 6000 6秒钟 url xxxxxxxxx type post su
  • mxnet for java_如何为mxnet安装R包?

    我曾经使用过R中的 mxnet 软件包 我有一个旧的安装 我可以在R 3 4x中使用它 但现在我想切换到最新的R版本 R 3 5本地 我很乐意在rstudio上使用它 Cloud 包裹在CRAN上 不是吗 现在我尝试按照包裹的说明page
  • 关于microsoft Store被删除了重新下载的问题

    分享一条连接 https jingyan baidu com article 4ae03de3b59d223eff9e6b39 html 另外补充一点 microsoftStore无法联网问题 https www pianshen com
  • 大数据是如何影响何改变我们生活的?

    大数据是如何影响何改变我们生活的 网上一则违法广告 普通市民可能一瞥而过 如今却逃不过重庆工商情报信息工作平台的 法眼 这正是因为重庆市工商局运用大数据 云计算 互联网 等技术手段 在全国工商系统率先建立的重庆工商情报信息工作平台 同样 重
  • 十分钟搭建一个EFK集群

    快速部署搭建ELK ELK 介绍 环境信息 架构 二进制部署 一 系统初始化 二 elasticsearch安装 所有节点 三 filebeat安装使用 四 nginx安装 五 kibana 安装 六访问elasticsearch出现yel
  • 卡方分布的latex代码

    网上没找到答案 自己看了一些还是mathcal字体比较像 最终选择mathcal字体 LaTeX LaTeX LATE X代码 编译示例 mathcal X 2 4 X
  • 深度理解取余/取模运算,你得知道这些...

    大家在日常编程或者学习中都会经常接触到取余 取模运算 那么计算机中取余 取模运算究竟是怎么定义的 读完本篇文章 相信你一点会有很大收获 文章目录 step 1 你可能不知道的四种 取整 方式 1 1 C语言中本质是 向0取整 1 2 flo
  • SpaCy下载及安装

    1 官网给的命令快速安装基本没用 结果就是一直拒绝你的连接 官网 https spacy io 官网是说用下面的两行命令就能够快速安装并使用spacy了 第一行命令是可以很快就成功 但是第二行就来坑了 报错如下图 报错内容是 request
  • 虚拟机的一些命令

    cd 目录名 cd 上一层目录 cd 回到跟目录 ls 当前目录下的内容 ls 目录名 指定目录下的内容 ls a 当前目录下的内容 隐藏内容 ls l 详细列出指定目录或者当前目录下的内容 pwd 查看当前所在的目录 创建目录 mkdir
  • kafka 由于网络原因,最后报错打开的文件过多 (org.apache.zookeeper.ClientCnxn)停止结束服务,求教! --- 采坑

    kafka 运行好好地 今天一用发现有一台服务停了 最后查看日志发现为网络原因 打开文件过多 代理停止 求教大佬解决方案 2019 05 01 17 59 08 224 INFO Socket error occurred 192 168
  • 开关电源基础03:正激和反激开关电源拓扑(1)-正激拓扑

    说在开头 关于薛定谔的波动方程 2 全新的量子理论诞生不到一年 很快面临着粒子和波动的内战 矩阵力学从直接观测到的原子谱线出发 引入矩阵的数学工具 建立了整个新力学的大厦 它强调观测到的分立性 跳跃性 同时又坚持以数学为唯一导向 不为日常生
  • 2016.6.5 计算机网络考试要点第一章之计算机网络性能

    1 8计算机网络性能指标 1 速率 比特率或数据率 比特是计算机中数据量的单位 也是信息论中使用的信息量的单位 意思是 一个二进制数字 因此一个比特就是二进制数字中的一个1或0 当数据率较高时 就可以用kb s k 10的3次方 千 Mb
  • Scanner类中next和nextLine方法的区别

    Scanner类中next和nextLine方法的区别 nextInt next nextDouble 从左往右扫描数据 在扫描到第一个有效字符之前会自动过滤结束标记符 扫描结束标记是 空格 tab键 回车符 不会过滤回车符 nextLin
  • MVC综合案例

    pojo Dept package com qf webpro pojo import java io Serializable public class Dept implements Serializable private int d
  • lua 协同程序(coroutine)

    本文主要是根据菜鸟教程的lua协同程序 coroutine 优化出来的文章 对于网上的lua协同程序的文章 觉得菜鸟教程里的lua协同程序 coroutine 比较完善 但还是有一些地方没有说清楚 对于像我这样的新手学习起来 还是不能完全解