什么是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协同程序示例,展示了如何创建、启动、挂起和恢复协同程序。

-- 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.生产者消费者模型

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

-- 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 接收处理,体现了协同程序在实际场景中的应用,展示了其在多任务处理方面的优势与灵活性。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容