|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. Lua线程(协程)基本概念
Lua中的线程实际上是一种协程(coroutine),与我们通常所说的操作系统级线程有本质区别。Lua协程是协作式的多任务处理机制,它们允许在单个线程内实现多个执行流程的切换,但这些流程不是并行执行的,而是在显式地让出控制权时进行切换。
Lua协程具有以下特点:
• 轻量级:创建和切换成本低
• 协作式:必须显式让出控制权
• 共享地址空间:所有协程共享相同的全局变量和环境
• 拥有独立的栈:每个协程有自己的调用栈
在Lua中,协程主要用于实现协作式多任务、迭代器、状态机等功能,是实现复杂控制流的重要工具。
2. Lua线程的创建方法
在Lua中,创建新线程(协程)主要通过以下几种方式:
2.1 使用coroutine.create()
coroutine.create()是最基本的协程创建方法,它接受一个函数作为参数,并返回一个新创建的协程对象。
- -- 创建一个简单的协程
- co = coroutine.create(function ()
- for i = 1, 5 do
- print("协程输出: " .. i)
- coroutine.yield() -- 让出控制权
- end
- end)
- -- 启动并运行协程
- coroutine.resume(co)
- coroutine.resume(co)
- coroutine.resume(co)
- coroutine.resume(co)
- coroutine.resume(co)
复制代码
2.2 使用coroutine.wrap()
coroutine.wrap()也创建一个协程,但它返回一个函数而不是协程对象。每次调用这个函数都会恢复协程的执行。
- -- 使用wrap创建协程
- co_func = coroutine.wrap(function ()
- for i = 1, 3 do
- print("wrap协程输出: " .. i)
- coroutine.yield()
- end
- end)
- -- 调用返回的函数来恢复协程
- co_func()
- co_func()
- co_func()
复制代码
2.3 带参数的协程创建
协程函数可以接受参数,这些参数在首次调用coroutine.resume()时传入:
- -- 创建带参数的协程
- co = coroutine.create(function (a, b)
- print("协程接收到参数:", a, b)
- local sum = a + b
- coroutine.yield(sum) -- 返回计算结果
-
- local product = a * b
- return product -- 最终返回值
- end)
- -- 首次恢复协程并传入参数
- local success, sum = coroutine.resume(co, 10, 20)
- print("协程第一次返回:", success, sum)
- -- 再次恢复协程
- local success, product = coroutine.resume(co)
- print("协程最终返回:", success, product)
复制代码
3. Lua线程的内存管理机制
Lua使用自动内存管理,主要通过垃圾收集器(GC)来管理内存。理解Lua的内存管理机制对于避免内存泄漏至关重要。
3.1 Lua垃圾收集器概述
Lua的垃圾收集器使用了一种增量式标记-清除(Mark-and-Sweep)算法,具有以下特点:
• 自动回收不再使用的内存
• 增量式执行,避免长时间暂停
• 可配置的GC参数(步长、步进倍率等)
3.2 Lua对象的生命周期
在Lua中,所有对象(包括协程)都是自动管理的。对象的生命周期通常如下:
1. 创建:当对象被创建时,内存被分配
2. 使用:对象被引用和使用
3. 不可达:当对象不再被任何变量引用时,成为”垃圾”
4. 回收:GC在适当的时候回收这些垃圾对象的内存
3.3 协程对象的内存结构
每个Lua协程对象包含以下内存结构:
• 独立的栈空间:存储协程的调用栈、局部变量等
• 状态信息:记录协程的当前状态(挂起、运行、死亡等)
• 引用环境:指向全局环境的引用
- -- 示例:查看协程的内存占用
- local function measure_memory()
- collectgarbage("collect") -- 先执行一次完整GC
- local before = collectgarbage("count") -- 获取当前内存使用量(KB)
-
- -- 创建多个协程
- local coroutines = {}
- for i = 1, 1000 do
- coroutines[i] = coroutine.create(function()
- local data = string.rep("x", 1000) -- 每个协程分配一些数据
- end)
- coroutine.resume(coroutines[i])
- end
-
- local after = collectgarbage("count")
- print("创建1000个协程前后内存使用:", before, "KB ->", after, "KB")
- print("内存差:", after - before, "KB")
-
- -- 清除引用
- coroutines = nil
- collectgarbage("collect")
- local final = collectgarbage("count")
- print("清理后内存使用:", final, "KB")
- end
- measure_memory()
复制代码
4. Lua线程的内存释放机制
Lua线程的内存释放主要依赖于垃圾收集器,但也有一些特殊情况需要注意。
4.1 正常情况下的内存释放
当一个协程对象不再被任何变量引用时,它将成为GC的回收目标:
- -- 示例:协程的正常内存释放
- local function create_and_release_coroutine()
- -- 创建协程
- local co = coroutine.create(function()
- local large_data = string.rep("x", 100000) -- 分配较大内存
- print("协程中的数据大小:", #large_data)
- coroutine.yield()
- end)
-
- -- 运行协程
- coroutine.resume(co)
-
- -- 协程运行后,large_data变量仍然在协程栈中
- -- 此时协程对象co仍然被引用,内存不会被释放
-
- -- 将co设为nil,移除引用
- co = nil
-
- -- 强制执行GC(仅用于演示,实际代码中通常不需要)
- collectgarbage("collect")
- print("协程对象已释放")
- end
- create_and_release_coroutine()
复制代码
4.2 协程状态与内存释放
协程的状态会影响其内存释放时机:
- -- 示例:不同状态协程的内存释放
- local function test_coroutine_states()
- -- 创建一个运行中的协程
- local running_co = coroutine.create(function()
- while true do
- coroutine.yield()
- end
- end)
- coroutine.resume(running_co)
-
- -- 创建一个已完成的协程
- local finished_co = coroutine.create(function()
- print("这个协程即将完成")
- end)
- coroutine.resume(finished_co)
-
- -- 创建一个挂起的协程
- local suspended_co = coroutine.create(function()
- coroutine.yield("挂起中")
- end)
- coroutine.resume(suspended_co)
-
- -- 检查协程状态
- print("running_co状态:", coroutine.status(running_co))
- print("finished_co状态:", coroutine.status(finished_co))
- print("suspended_co状态:", coroutine.status(suspended_co))
-
- -- 移除所有引用
- running_co = nil
- finished_co = nil
- suspended_co = nil
-
- -- 强制GC
- collectgarbage("collect")
- print("所有协程引用已移除")
- end
- test_coroutine_states()
复制代码
4.3 协程中的闭包与内存释放
协程中使用的闭包可能会影响内存释放:
- -- 示例:协程中的闭包与内存
- local function coroutine_with_closures()
- local large_data = string.rep("x", 100000) -- 大数据
-
- -- 创建一个闭包函数
- local closure = function()
- print("闭包访问大数据:", #large_data)
- end
-
- -- 创建协程并使用闭包
- local co = coroutine.create(function()
- closure() -- 使用闭包
- coroutine.yield()
- end)
-
- coroutine.resume(co)
-
- -- 即使协程完成,large_data仍然被闭包引用
- -- 需要确保闭包也被释放
-
- -- 移除引用
- co = nil
- closure = nil
- large_data = nil
-
- collectgarbage("collect")
- print("闭包和协程已释放")
- end
- coroutine_with_closures()
复制代码
5. 常见的内存泄漏问题和原因
在Lua中使用协程时,可能会遇到一些常见的内存泄漏问题。
5.1 循环引用导致的内存泄漏
当协程之间或协程与其他对象之间存在循环引用时,可能导致内存泄漏:
- -- 示例:协程间的循环引用
- local function circular_reference_leak()
- local co1, co2
-
- -- 创建第一个协程
- co1 = coroutine.create(function()
- -- 在协程1中引用协程2
- local ref_to_co2 = co2
- print("协程1引用协程2")
- coroutine.yield()
- end)
-
- -- 创建第二个协程
- co2 = coroutine.create(function()
- -- 在协程2中引用协程1
- local ref_to_co1 = co1
- print("协程2引用协程1")
- coroutine.yield()
- end)
-
- -- 运行协程
- coroutine.resume(co1)
- coroutine.resume(co2)
-
- -- 即使将co1和co2设为nil,由于协程内部相互引用
- -- 它们可能不会被GC回收
- co1 = nil
- co2 = nil
-
- -- 强制GC
- collectgarbage("collect")
- print("注意:循环引用可能导致协程无法被GC回收")
- end
- circular_reference_leak()
复制代码
5.2 未完成的协程导致的内存泄漏
如果创建了大量协程但没有让它们正常结束,可能导致内存泄漏:
- -- 示例:未完成协程导致的内存泄漏
- local function unfinished_coroutine_leak()
- local coroutines = {}
-
- -- 创建1000个永远不会结束的协程
- for i = 1, 1000 do
- coroutines[i] = coroutine.create(function()
- while true do
- coroutine.yield()
- end
- end)
- coroutine.resume(coroutines[i])
- end
-
- -- 测量内存使用
- collectgarbage("collect")
- local mem_with_coroutines = collectgarbage("count")
- print("1000个未完成协程的内存使用:", mem_with_coroutines, "KB")
-
- -- 即使将coroutines表设为nil,如果协程仍在运行
- -- 它们可能不会被立即回收
- coroutines = nil
-
- -- 强制GC
- collectgarbage("collect")
- local mem_after_clear = collectgarbage("count")
- print("清除引用后的内存使用:", mem_after_clear, "KB")
-
- -- 注意:未完成的协程可能仍然占用内存
- -- 正确做法是确保协程能够正常结束
- end
- unfinished_coroutine_leak()
复制代码
5.3 协程栈中保留大数据导致的内存泄漏
如果协程的栈中保留了大量数据,即使协程不再被引用,这些数据也可能不会被及时释放:
- -- 示例:协程栈中保留大数据
- local function large_data_in_stack()
- local function create_coroutine_with_large_data()
- -- 在协程中创建大数据
- local large_data = string.rep("x", 1000000) -- 1MB数据
-
- -- 执行一些操作
- local processed_data = string.upper(large_data)
-
- -- 让出控制权,但large_data和processed_data仍在栈中
- coroutine.yield()
-
- -- 返回处理结果
- return processed_data
- end
-
- -- 创建并运行协程
- local co = coroutine.create(create_coroutine_with_large_data)
- coroutine.resume(co)
-
- -- 协程挂起,但栈中的大数据仍然存在
- -- 即使协程对象被引用,这些数据也会占用内存
-
- -- 完成协程
- local success, result = coroutine.resume(co)
- print("协程完成,结果大小:", #result)
-
- -- 现在可以安全地释放协程
- co = nil
- result = nil
-
- collectgarbage("collect")
- print("协程和数据已释放")
- end
- large_data_in_stack()
复制代码
6. 如何正确管理Lua线程以避免内存泄漏
为了避免Lua协程相关的内存泄漏,需要采取一些正确的管理策略。
6.1 确保协程正常结束
确保协程能够正常结束是最基本的内存管理策略:
- -- 示例:确保协程正常结束
- local function ensure_coroutine_completion()
- local coroutines = {}
-
- -- 创建多个协程,但确保它们能够结束
- for i = 1, 10 do
- coroutines[i] = coroutine.create(function(count)
- for j = 1, count do
- print(string.format("协程%d: 步骤%d", i, j))
- coroutine.yield()
- end
- print(string.format("协程%d: 完成", i))
- end)
- end
-
- -- 运行所有协程直到它们完成
- local all_done = false
- while not all_done do
- all_done = true
- for i, co in ipairs(coroutines) do
- if coroutine.status(co) ~= "dead" then
- coroutine.resume(co, 5) -- 每个协程运行5步
- all_done = false
- end
- end
- end
-
- -- 所有协程已完成,可以安全释放
- coroutines = nil
- collectgarbage("collect")
- print("所有协程已完成并释放")
- end
- ensure_coroutine_completion()
复制代码
6.2 使用弱引用表管理协程
弱引用表可以帮助管理协程,避免不必要的引用阻止GC回收:
- -- 示例:使用弱引用表管理协程
- local function weak_table_management()
- -- 创建一个弱引用表,value为弱引用
- local weak_coroutines = setmetatable({}, {__mode = "v"})
-
- -- 创建并添加协程到弱引用表
- for i = 1, 5 do
- local co = coroutine.create(function()
- print(string.format("协程%d运行中", i))
- coroutine.yield()
- print(string.format("协程%d完成", i))
- end)
-
- -- 使用协程ID作为key,协程对象作为value
- weak_coroutines[i] = co
- coroutine.resume(co)
- end
-
- -- 强制GC,观察弱引用表中的协程是否被回收
- collectgarbage("collect")
- print("GC后弱引用表中的协程数量:", #weak_coroutines)
-
- -- 手动完成所有协程
- for i, co in pairs(weak_coroutines) do
- if coroutine.status(co) ~= "dead" then
- coroutine.resume(co)
- end
- end
-
- -- 再次强制GC
- collectgarbage("collect")
- print("所有协程完成后,弱引用表中的协程数量:", #weak_coroutines)
-
- -- 清除弱引用表
- weak_coroutines = nil
- collectgarbage("collect")
- print("弱引用表已清理")
- end
- weak_table_management()
复制代码
6.3 避免在协程中创建不必要的闭包
减少协程中不必要的闭包可以降低内存泄漏的风险:
- -- 示例:避免不必要的闭包
- local function avoid_unnecessary_closures()
- -- 不好的做法:在协程中创建闭包捕获外部变量
- local function bad_practice()
- local external_data = "外部数据"
-
- local co = coroutine.create(function()
- -- 这个闭包捕获了external_data
- local closure = function()
- print("访问外部数据:", external_data)
- end
-
- closure()
- coroutine.yield()
- end)
-
- coroutine.resume(co)
- -- 即使协程完成,external_data可能不会被释放
- -- 因为闭包仍然引用它
-
- co = nil
- collectgarbage("collect")
- end
-
- -- 好的做法:避免不必要的闭包
- local function good_practice()
- local co = coroutine.create(function()
- -- 直接使用局部变量,而不是闭包
- local local_data = "局部数据"
- print("使用局部数据:", local_data)
- coroutine.yield()
- end)
-
- coroutine.resume(co)
- -- 没有不必要的闭包,内存更容易释放
-
- co = nil
- collectgarbage("collect")
- end
-
- print("测试不好的做法:")
- bad_practice()
-
- print("\n测试好的做法:")
- good_practice()
- end
- avoid_unnecessary_closures()
复制代码
6.4 使用协程池管理协程生命周期
协程池是一种有效管理协程生命周期的模式,特别适用于需要频繁创建和销毁协程的场景:
- -- 示例:协程池实现
- local CoroutinePool = {
- pools = {},
- maxPoolSize = 10, -- 每种类型协程的最大池大小
- }
- -- 创建或获取协程池
- function CoroutinePool.getPool(type)
- if not CoroutinePool.pools[type] then
- CoroutinePool.pools[type] = {}
- end
- return CoroutinePool.pools[type]
- end
- -- 从池中获取协程
- function CoroutinePool.acquire(type, func)
- local pool = CoroutinePool.getPool(type)
-
- if #pool > 0 then
- -- 从池中取出一个协程
- local co = table.remove(pool)
- -- 重置协程的函数
- -- 注意:Lua 5.1+ 不支持直接重置协程函数,这里只是示例
- -- 实际实现可能需要使用其他方法
- return co
- else
- -- 创建新协程
- return coroutine.create(func)
- end
- end
- -- 将协程返回池中
- function CoroutinePool.release(type, co)
- local pool = CoroutinePool.getPool(type)
-
- -- 检查协程状态和池大小
- if coroutine.status(co) == "dead" or #pool >= CoroutinePool.maxPoolSize then
- -- 如果协程已结束或池已满,不回收
- return
- end
-
- -- 将协程放回池中
- table.insert(pool, co)
- end
- -- 清理协程池
- function CoroutinePool.clear()
- for type, pool in pairs(CoroutinePool.pools) do
- CoroutinePool.pools[type] = {}
- end
- collectgarbage("collect")
- end
- -- 使用协程池的示例
- local function use_coroutine_pool()
- -- 定义协程函数
- local task_func = function(id)
- print(string.format("任务%d开始", id))
- coroutine.yield()
- print(string.format("任务%d继续", id))
- coroutine.yield()
- print(string.format("任务%d完成", id))
- end
-
- -- 获取并使用协程
- local co1 = CoroutinePool.acquire("task", task_func)
- coroutine.resume(co1, 1)
-
- local co2 = CoroutinePool.acquire("task", task_func)
- coroutine.resume(co2, 2)
-
- -- 继续执行协程
- coroutine.resume(co1)
- coroutine.resume(co2)
-
- -- 完成协程
- coroutine.resume(co1)
- coroutine.resume(co2)
-
- -- 释放协程回池中
- CoroutinePool.release("task", co1)
- CoroutinePool.release("task", co2)
-
- -- 再次获取协程(应该从池中获取)
- local co3 = CoroutinePool.acquire("task", task_func)
- coroutine.resume(co3, 3)
- coroutine.resume(co3)
- coroutine.resume(co3)
-
- -- 释放协程
- CoroutinePool.release("task", co3)
-
- -- 清理池
- CoroutinePool.clear()
- print("协程池已清理")
- end
- use_coroutine_pool()
复制代码
7. 最佳实践和代码示例
7.1 协程生命周期管理最佳实践
- -- 示例:协程生命周期管理最佳实践
- local function coroutine_lifecycle_management()
- -- 1. 使用协程管理器来跟踪所有活动协程
- local CoroutineManager = {
- active_coroutines = {},
- next_id = 1
- }
-
- -- 注册新协程
- function CoroutineManager.register(func, ...)
- local co = coroutine.create(func)
- local id = CoroutineManager.next_id
- CoroutineManager.next_id = CoroutineManager.next_id + 1
-
- -- 存储协程及其元数据
- CoroutineManager.active_coroutines[id] = {
- coroutine = co,
- status = "running",
- created_at = os.time()
- }
-
- -- 启动协程并传递参数
- local success, err = coroutine.resume(co, ...)
- if not success then
- print("协程启动错误:", err)
- CoroutineManager.active_coroutines[id] = nil
- return nil
- end
-
- return id
- end
-
- -- 更新所有协程
- function CoroutineManager.update()
- local dead_coroutines = {}
-
- for id, co_data in pairs(CoroutineManager.active_coroutines) do
- local co = co_data.coroutine
-
- if coroutine.status(co) ~= "dead" then
- -- 恢复协程
- local success, err = coroutine.resume(co)
- if not success then
- print("协程执行错误:", err)
- table.insert(dead_coroutines, id)
- end
- else
- -- 标记死亡的协程
- table.insert(dead_coroutines, id)
- end
- end
-
- -- 清理死亡的协程
- for _, id in ipairs(dead_coroutines) do
- CoroutineManager.active_coroutines[id] = nil
- end
- end
-
- -- 获取活动协程数量
- function CoroutineManager.get_active_count()
- local count = 0
- for _ in pairs(CoroutineManager.active_coroutines) do
- count = count + 1
- end
- return count
- end
-
- -- 清理所有协程
- function CoroutineManager.clear()
- for id, co_data in pairs(CoroutineManager.active_coroutines) do
- -- 强制结束协程(仅用于清理)
- -- 注意:这不是一个安全的操作,实际应用中可能需要更优雅的处理方式
- CoroutineManager.active_coroutines[id] = nil
- end
- collectgarbage("collect")
- end
-
- -- 2. 创建示例任务
- local function sample_task(id, duration)
- print(string.format("任务%d开始,预计运行%d个周期", id, duration))
-
- for i = 1, duration do
- print(string.format("任务%d: 周期%d/%d", id, i, duration))
- coroutine.yield()
- end
-
- print(string.format("任务%d完成", id))
- end
-
- -- 3. 使用协程管理器
- -- 注册多个任务
- local task1_id = CoroutineManager.register(sample_task, 1, 3)
- local task2_id = CoroutineManager.register(sample_task, 2, 5)
- local task3_id = CoroutineManager.register(sample_task, 3, 2)
-
- print("初始活动协程数量:", CoroutineManager.get_active_count())
-
- -- 模拟多个更新周期
- for i = 1, 6 do
- print(string.format("\n--- 更新周期%d ---", i))
- CoroutineManager.update()
- print("当前活动协程数量:", CoroutineManager.get_active_count())
- end
-
- -- 清理
- CoroutineManager.clear()
- print("协程管理器已清理")
- end
- coroutine_lifecycle_management()
复制代码
7.2 内存监控与调试
- -- 示例:协程内存监控与调试
- local function coroutine_memory_monitoring()
- -- 内存监控工具
- local MemoryMonitor = {
- history = {},
- max_history = 100
- }
-
- -- 记录内存使用情况
- function MemoryMonitor.record()
- collectgarbage("collect")
- local mem_kb = collectgarbage("count")
- table.insert(MemoryMonitor.history, mem_kb)
-
- -- 保持历史记录在限制范围内
- if #MemoryMonitor.history > MemoryMonitor.max_history then
- table.remove(MemoryMonitor.history, 1)
- end
-
- return mem_kb
- end
-
- -- 获取内存使用趋势
- function MemoryMonitor.get_trend()
- if #MemoryMonitor.history < 2 then
- return "insufficient_data"
- end
-
- local first = MemoryMonitor.history[1]
- local last = MemoryMonitor.history[#MemoryMonitor.history]
-
- if last > first * 1.1 then
- return "increasing"
- elseif last < first * 0.9 then
- return "decreasing"
- else
- return "stable"
- end
- end
-
- -- 打印内存使用报告
- function MemoryMonitor.report()
- local trend = MemoryMonitor.get_trend()
- local current = MemoryMonitor.record()
-
- print("\n--- 内存使用报告 ---")
- print("当前内存使用:", string.format("%.2f KB", current))
- print("内存使用趋势:", trend)
-
- if trend == "increasing" then
- print("警告: 内存使用量正在增加,可能存在内存泄漏")
- end
- end
-
- -- 1. 记录初始内存
- MemoryMonitor.record()
- print("初始内存使用:", string.format("%.2f KB", MemoryMonitor.history[1]), "KB")
-
- -- 2. 创建一些协程并监控内存
- local coroutines = {}
-
- -- 创建10个简单协程
- for i = 1, 10 do
- coroutines[i] = coroutine.create(function()
- local data = string.rep("x", 1000) -- 分配一些数据
- for j = 1, 5 do
- coroutine.yield()
- end
- end)
- coroutine.resume(coroutines[i])
-
- -- 每创建一个协程后记录内存
- MemoryMonitor.record()
- end
-
- -- 3. 运行协程并监控内存
- for round = 1, 5 do
- print("\n--- 运行轮次", round, "---")
- for i, co in ipairs(coroutines) do
- if coroutine.status(co) ~= "dead" then
- coroutine.resume(co)
- end
- end
-
- MemoryMonitor.record()
- MemoryMonitor.report()
- end
-
- -- 4. 清理协程并监控内存
- print("\n--- 清理协程 ---")
- coroutines = nil
- collectgarbage("collect")
-
- MemoryMonitor.record()
- MemoryMonitor.report()
-
- -- 5. 创建一个可能导致内存泄漏的场景
- print("\n--- 模拟内存泄漏场景 ---")
- local leaky_coroutines = {}
-
- for i = 1, 20 do
- -- 创建协程但不保存引用,理论上应该被GC回收
- coroutine.create(function()
- -- 创建一些数据
- local data = {}
- for j = 1, 100 do
- data[j] = string.rep("y", 1000)
- end
-
- -- 无限循环,协程永远不会结束
- while true do
- coroutine.yield()
- end
- end)
-
- MemoryMonitor.record()
- end
-
- MemoryMonitor.report()
-
- -- 6. 强制GC并查看结果
- print("\n--- 强制垃圾回收 ---")
- collectgarbage("collect")
- MemoryMonitor.record()
- MemoryMonitor.report()
-
- -- 注意:由于无限循环的协程仍然存在,内存可能不会完全释放
- -- 这是一个典型的内存泄漏场景
- end
- coroutine_memory_monitoring()
复制代码
7.3 综合示例:协程安全的资源管理
- -- 示例:协程安全的资源管理
- local function coroutine_safe_resource_management()
- -- 资源管理器
- local ResourceManager = {
- resources = {},
- resource_counter = 0,
- -- 使用弱值表来跟踪资源,避免阻止GC
- resource_tracking = setmetatable({}, {__mode = "v"})
- }
-
- -- 创建新资源
- function ResourceManager.create(data)
- ResourceManager.resource_counter = ResourceManager.resource_counter + 1
- local id = ResourceManager.resource_counter
-
- -- 创建资源对象
- local resource = {
- id = id,
- data = data,
- created_at = os.time(),
- ref_count = 0,
- -- 清理函数
- cleanup = function(self)
- print(string.format("清理资源%d (数据: %s)", self.id, self.data))
- ResourceManager.resources[self.id] = nil
- end
- }
-
- -- 存储资源
- ResourceManager.resources[id] = resource
-
- return resource
- end
-
- -- 获取资源(增加引用计数)
- function ResourceManager.acquire(id)
- local resource = ResourceManager.resources[id]
- if resource then
- resource.ref_count = resource.ref_count + 1
- return resource
- end
- return nil
- end
-
- -- 释放资源(减少引用计数)
- function ResourceManager.release(resource)
- if resource and ResourceManager.resources[resource.id] then
- resource.ref_count = resource.ref_count - 1
- if resource.ref_count <= 0 then
- resource:cleanup()
- end
- end
- end
-
- -- 协程安全的资源使用包装器
- function ResourceManager.with_resource(id, func)
- local resource = ResourceManager.acquire(id)
- if not resource then
- error("资源不存在: " .. tostring(id))
- end
-
- -- 使用pcall确保资源总是被释放
- local success, result = pcall(func, resource)
- ResourceManager.release(resource)
-
- if not success then
- error(result)
- end
-
- return result
- end
-
- -- 1. 创建一些资源
- local res1 = ResourceManager.create("数据库连接")
- local res2 = ResourceManager.create("文件句柄")
- local res3 = ResourceManager.create("网络套接字")
-
- -- 2. 创建使用资源的协程
- local function resource_user_task(resource_id, task_name)
- print(string.format("任务'%s'开始,需要资源%d", task_name, resource_id))
-
- -- 使用with_resource确保资源正确释放
- ResourceManager.with_resource(resource_id, function(resource)
- print(string.format("任务'%s'正在使用资源%d (数据: %s)",
- task_name, resource.id, resource.data))
-
- -- 模拟工作
- for i = 1, 3 do
- print(string.format("任务'%s': 工作步骤%d", task_name, i))
- coroutine.yield()
- end
-
- print(string.format("任务'%s'完成资源使用", task_name))
- end)
-
- print(string.format("任务'%s'完成", task_name))
- end
-
- -- 3. 创建协程
- local tasks = {
- coroutine.create(function() resource_user_task(res1.id, "数据处理任务") end),
- coroutine.create(function() resource_user_task(res2.id, "文件读写任务") end),
- coroutine.create(function() resource_user_task(res3.id, "网络通信任务") end),
- coroutine.create(function() resource_user_task(res1.id, "备份任务") end),
- coroutine.create(function() resource_user_task(res2.id, "日志任务") end)
- }
-
- -- 4. 运行协程直到所有完成
- local all_done = false
- local round = 0
-
- while not all_done do
- round = round + 1
- print(string.format("\n--- 执行轮次 %d ---", round))
-
- all_done = true
- for _, task in ipairs(tasks) do
- if coroutine.status(task) ~= "dead" then
- coroutine.resume(task)
- all_done = false
- end
- end
-
- -- 打印资源状态
- print("当前资源状态:")
- for id, resource in pairs(ResourceManager.resources) do
- print(string.format(" 资源%d: 引用计数=%d", id, resource.ref_count))
- end
- end
-
- -- 5. 清理
- print("\n--- 最终清理 ---")
- for id, resource in pairs(ResourceManager.resources) do
- resource:cleanup()
- end
-
- print("资源管理完成")
- end
- coroutine_safe_resource_management()
复制代码
结论
Lua协程是一种强大的工具,但正确管理其内存使用至关重要。通过理解Lua的内存管理机制、识别常见的内存泄漏原因,并采用适当的管理策略,可以有效地避免内存泄漏问题。
关键要点包括:
1. 确保协程能够正常结束,避免无限循环的协程
2. 注意协程中的闭包和引用,避免不必要的引用阻止GC回收
3. 使用协程池等模式重用协程,减少创建和销毁的开销
4. 实施适当的监控和调试机制,及时发现内存问题
5. 使用资源管理器等模式确保资源正确释放
通过遵循这些最佳实践,可以充分利用Lua协程的强大功能,同时避免内存泄漏等常见问题。 |
|