当前位置: 首页 > article >正文

Lua | 每日一练 (4)

💢欢迎来到张胤尘的技术站
💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥

文章目录

  • Lua | 每日一练 (4)
    • 题目
    • 参考答案
      • 线程和协程
        • 调度方式
        • 上下文切换
        • 资源占用
        • 实现机制
        • 使用场景
      • `lua` 中的协程
        • 协程的生命周期
        • 主要函数
          • 创建协程
          • 启动或恢复协程
          • 检查当前是否在主协程中运行
          • 暂停协程
          • 检测协程是否可暂停
          • 获取协程状态
          • 包装函数
          • 关闭协程
        • 具体使用

Lua | 每日一练 (4)

题目

协程和线程有何区别?简述 lua 中的协程。

参考答案

线程和协程

协程和线程虽然在某些方面有相似之处,但它们在设计目标、实现原理和使用方式上有很大的区别。下面从调用方式、上下文切换、资源使用、实现机制、使用场景这几个方面进行阐述。

调度方式
  • 线程:线程的执行由操作系统内核控制,操作系统会根据调度算法(如时间片轮转、优先级调度等)自动切换线程的执行。另外,线程的切换时间点不可预测,程序无法直接控制线程的暂停和恢复。
  • 协程:协程的执行由程序显式控制,需要开发者通过 coroutine.yieldcoroutine.resume 显式地暂停和恢复协程。协程的切换完全由程序逻辑决定,切换点是明确的。
上下文切换
  • 线程:线程切换涉及操作系统内核的上下文切换,需要保存和恢复线程的寄存器状态、栈信息等,开销较大。
  • 协程:协程的上下文切换在用户态完成,不需要操作系统内核介入,开销非常小。
资源占用
  • 线程:每个线程都有自己的栈空间,通常默认分配 8 MB,资源占用较大。如果线程数量过多会导致系统资源耗尽。
$ ulimit -s
8192
  • 协程:协程的栈空间是动态分配的,通常占用较少的内存。另外,协程的数量可以非常大,适合处理大规模的并发任务。
实现机制
  • 线程:线程是操作系统提供的并发机制,由操作系统内核管理。线程的创建和销毁需要系统调用,涉及内核态和用户态的切换。
  • 协程:协程是语言层面的机制,由 lua 解释器实现。协程的创建和切换完全在用户态完成,不涉及操作系统内核。
使用场景
  • 线程:适合处理真正的并发任务,例如多核 CPU 上的并行计算,处理 I/O 密集型任务。
  • 协程:适合处理单核 CPU 上的并发任务,尤其是需要频繁切换的场景;适合实现非阻塞 I/O 操作,例如网络编程中的异步请求处理。

lua 中的协程

lua 中,协程是实现异步编程的核心工具之一。由于 lua 本身没有内置的多线程支持,而协程提供了一种轻量级的并发机制,可以用来模拟异步操作,从而实现非阻塞的程序设计。

协程的生命周期

lua 协程的生命周期包括以下几个阶段:

  • 创建:使用 coroutine.create 创建一个协程。此时协程处于挂起状态,尚未开始执行。
  • 运行:使用 coroutine.resume 启动或恢复协程的执行。
  • 暂停:在协程执行过程中,可以通过 coroutine.yield 暂停协程的执行,将控制权交回主程序。
  • 结束:当协程运行完成或因错误终止时,协程进入结束状态。

协程的状态之间切换,如下图所示:

在这里插入图片描述

主要函数

下面介绍 lua 中关于协程的主要函数。

创建协程
local co = coroutine.create(f)
  • f:协程函数,表示协程的主体逻辑。
  • 返回一个协程对象 co,类型为 thread。协程对象可以用于后续的 coroutine.resumecoroutine.yield 等操作。

例如:

-- func 是协程函数,主体逻辑
local function func()
    print("Coroutine is running")
end

-- 创建协程
local co = coroutine.create(func)
print(co) -- thread: 0x60f0c8666df8
启动或恢复协程
ok, ... = coroutine.resume(co, ...)
  • co:要启动或者恢复的协程对象。
  • ...:可选参数,这些参数会传递给协程中的 coroutine.yield 或协程的入口函数。
  • ok:布尔值,表示协程是否成功恢复。如果协程因错误终止,返回 false
  • ...:协程中 coroutine.yield 的返回值。如果协程运行完成,返回值为 nil

说明:

  • 如果协程处于 suspended 状态,coroutine.resume 会启动或恢复协程的执行。
  • 如果协程已经处于 running 状态,调用 coroutine.resume 会抛出错误。
  • 如果协程已经处于 dead 状态,调用 coroutine.resume 也会抛出错误。

例如:

local function func(x, y)
    print("Coroutine started with:", x, y) -- Coroutine started with:	10	20
end

local co = coroutine.create(func)

local ok, result = coroutine.resume(co, 10, 20)
print(ok, result) -- true	nil
检查当前是否在主协程中运行
current_co, is_main = coroutine.running()
  • current_co:当前运行的协程对象。
  • is_main:布尔值,表示当前是否在主协程中运行,如果是主协程返回 true,否则返回 false

说明:

  • 用于检查当前是否在主协程中运行,以及当前协程是否是主线程。

例如:

local function printCurrentCoroutine()
    local current_co, is_main = coroutine.running()
    print(current_co, is_main)
end

printCurrentCoroutine() -- thread: 0x61617e0492a8	true

local co = coroutine.create(function()
    print("Printing from the coroutine") -- Printing from the coroutine
    printCurrentCoroutine()              -- thread: 0x61617e04fec8	false
end)

coroutine.resume(co) -- 启动协程
暂停协程
... = coroutine.yield(...)
  • ...:可选参数,这些参数会传递给调用 coroutine.resume 的代码。
  • 返回值是 coroutine.resume 调用时传递的参数。

说明:

  • coroutine.yield 用于暂停当前协程的执行,并将控制权返回给调用 coroutine.resume 的代码。
  • 协程暂停后,可以通过再次调用 coroutine.resume 恢复执行。

例如:

local function func()
    print("Coroutine running")              -- Coroutine running
    local value = coroutine.yield("Yielded value")
    print("Coroutine resumed with:", value) -- Coroutine resumed with:	Hello
end

local co = coroutine.create(func)
local ok, result = coroutine.resume(co)
print(ok, result) -- true	Yielded value

local ok, result = coroutine.resume(co, "Hello")
print(ok, result) -- true	nil
检测协程是否可暂停
is_yieldable = coroutine.isyieldable([co])
  • co:可选参数,表示要检查的协程对象。默认为当前运行的协程。
  • is_yieldable:布尔值,表示协程是否可以暂停。

说明:

  • 如果协程不是主协程且不在非可暂停的 C 函数中,则返回 true
  • 主协程调用时返回 false
  • 协程处于 suspendeddead 状态时 coroutine.isyieldable 仍然返回 true,因为协程对象本身仍然是有效的,并且在 lua 的语义中,dead 状态的协程仍然可以被认为是可以暂停的(尽管它已经无法再被恢复)。

例如:

local co = coroutine.create(function()
    print(coroutine.isyieldable()) -- true
    coroutine.yield()
end)

coroutine.resume(co)             -- 启动协程
print(coroutine.isyieldable(co)) -- true
print(coroutine.status(co))      -- suspended
coroutine.resume(co)
print(coroutine.status(co))      -- dead
print(coroutine.isyieldable(co)) -- true
print(coroutine.isyieldable())   -- false
获取协程状态
status = coroutine.status(co)
  • co:要检查状态的协程对象。
  • 返回一个字符串,表示协程的当前状态:
    • `running:协程正在运行。
    • suspended:协程处于暂停状态。
    • dead:协程已经运行完成或因错误终止。
    • normal:协程尚未启动(初始状态)。

例如:

local function func(co)
    local status = coroutine.status(co)
    print(status) -- running
    coroutine.yield("hello, world!")
end

local co = coroutine.create(func)
local status = coroutine.status(co)
print(status) -- suspended

local ok, result = coroutine.resume(co, co)
print(ok, result) -- true	hello, world!

local ok, result = coroutine.resume(co, co)
print(ok, result) -- true	nil

local status = coroutine.status(co)
print(status) -- dead

包装函数
f = coroutine.wrap(f)
  • f:协程函数,表示协程的主体逻辑。
  • 返回一个包装函数 f。调用这个包装函数时,会自动启动或恢复协程的执行。

说明:

  • coroutine.wrapcoroutine.createcoroutine.resume 的简化版本。
  • 包装函数的返回值是协程中 coroutine.yield 的参数。
  • 如果协程运行完成,包装函数返回 nil;如果协程因错误终止,会抛出错误。

例如:

local wrapped = coroutine.wrap(function()
    print("Coroutine running")
    local value = coroutine.yield("Yielded value")
    print("Coroutine resumed with:", value)
end)

local result = wrapped()
print(result)    -- Yielded value

wrapped("Hello") -- Coroutine resumed with:	Hello
关闭协程
ok, err = coroutine.close(co)
  • co:要关闭的协程对象,协程的状态必须是 suspendeddead 状态。

  • ok:布尔值,表示操作是否成功。

  • err:如果操作失败,返回错误信息。

说明:

  • 关闭协程 co,将其状态设置为 dead,并关闭协程中所有待关闭的变量。
  • 如果协程已经是 dead 状态,调用 coroutine.close 会返回 true

例如:

local co = coroutine.create(function()
    coroutine.yield() -- 暂停协程
end)

coroutine.resume(co)                -- 启动协程
print(coroutine.status(co))         -- suspended

local ok, err = coroutine.close(co) -- 关闭协程
print(ok, err)                      -- true	nil
print(coroutine.status(co))         -- dead
具体使用

使用协程实现经典的生产者-消费者模型。协程的暂停和恢复特性非常适合这种场景,因为生产者和消费者可以分别在协程中运行,通过共享队列进行通信。

一个简单的生产者-消费者模型实现,如下所示:

-- 队列实现
local Queue = {}

function Queue:new()
    local obj = {}
    setmetatable(obj, self)
    self.__index = self
    obj.list = {}
    return obj
end

function Queue:put(item)
    table.insert(self.list, item)
end

function Queue:get()
    if #self.list <= 0 then
        return nil
    end

    local item = self.list[1]
    table.remove(self.list, 1)
    return item
end

function Queue:is_empty()
    return #self.list == 0
end

-- 生产者函数
local function producer(queue, count)
    for i = 1, count do
        print("Produced:", i)
        queue:put(i)
        coroutine.yield()
    end
end

-- 消费者函数
local function consumer(queue, count)
    for i = 1, count do
        while queue:is_empty() do
            print("Waiting for item...")
            coroutine.yield()
        end
        local item = queue:get()
        print("Consumed:", item)
    end
end

local queue = Queue:new()
local count = 100 -- 生产/消费数量

-- 创建生产者和消费者协程
local co_producer = coroutine.create(function()
    producer(queue, count)
end)

local co_consumer = coroutine.create(function()
    consumer(queue, count)
end)

-- 同时运行生产者和消费者协程
while coroutine.status(co_producer) ~= "dead" or coroutine.status(co_consumer) ~= "dead" do
    if coroutine.status(co_producer) ~= "dead" then
        coroutine.resume(co_producer)
    end
    if coroutine.status(co_consumer) ~= "dead" then
        coroutine.resume(co_consumer)
    end
end

🌺🌺🌺撒花!

如果本文对你有帮助,就点关注或者留个👍
如果您有任何技术问题或者需要更多其他的内容,请随时向我提问。

在这里插入图片描述


http://www.kler.cn/a/570428.html

相关文章:

  • mapbox基础,使用geojson加载heatmap热力图层
  • 利用Java爬虫获取1688店铺所有商品信息:实战指南
  • 3. 前后端实现压缩包文件下载
  • 汽车电子电控软件开发中因复杂度提升导致的架构恶化问题
  • EVOAGENT: Towards Automatic Multi-Agent Generation via Evolutionary Algorithms
  • 【Linux】信号保存
  • JavaWeb后端基础(5)
  • go语言转换json字符串为json数据
  • Spring统一格式返回
  • win11编译pytorchvision cuda128版本流程
  • 物联网桥梁监测设备集成GPS和红外
  • visual Studio Code安装
  • Express + MongoDB 实现视频点播
  • 准备好了数据集之后,如何在ubuntu22.04上训练一个yolov8模型。
  • STM32-GPIO详解
  • 解密 Faster R-CNN:高效目标检测的两阶段框架
  • 科普|无人机专业术语
  • LangChain4j 提取用户提问信息结构化输出 | LangChain4j
  • 散户如何实现自动化交易下单——篇1:体系介绍与获取同花顺资金账户和持仓信息
  • ArcGIS Pro实战技巧:如何利用进行科学选房分析