什么是协程?
从多线程的角度看,协程(Coroutine)与线程(thread)类似:协程是一系列的可执行语句,拥有自己的栈、局部变量和指令指针,同时协程又与其它协程共享全局变量和其它几乎一切资源。
线程和协程的主要区别:
一个多线程程序可以同时并行运行多条线程,而协程却需要彼此协作地运行,即在任意时刻只能有一个协程运行,且协程的切换是在用户态手动控制的,只有当正在运行的协程显示的要求被挂起(suspend)时,其执行才会暂停。
![什么是协程,线程和携程的主要区别有哪些? 图片[1]-什么是协程,线程和携程的主要区别有哪些?-不念博客](https://www.bunian.cn/wp-content/uploads/2022/12/image-344-189.png)
Lua中的协程支持
提供库函数
Lua中的所有协程相关函数都放在coroutine表中。
注:中括号为可选参数
函数原型 | 作用 | 补充 |
---|---|---|
coroutine.create(函数对象) -> 协程对象 | 创建一个协程对象并返回 | type(协程对象) = thread |
coroutine.resume(协程对象, [传递给协程函数的参数1,2,3..]) -> state, value | 开始/继续执行一个协程 | 正常执行:返回true和yield返回值 ; 发生一个未捕获错误:返回false和错误信息 |
coroutine.yield([返回值]) | 挂起当前正在运行的协程 | |
coroutine.status(协程对象) -> status | 返回协程状态 | create和yield后为挂起(suspended);执行过程为运行(running);执行完毕为死亡(dead);在A协程中唤醒B协程,这时A协程就为正常(normal)状态。 |
coroutine.wrap(协程主体函数) -> function | 创建一个以传入函数作为主体函数的新协程,并返回一个函数 | 返回的函数类似resume,不同的是调用不会返回state只返回value,如果出错会抛出异常。 |
使用样例
如果我们需要计算并打印一段斐波那契数列,假设每一步计算都需要一秒钟时间,传统写法可以是这样:
function Fibonacci(n)local tab = {}local a, b = 1, 1for i = 1, n dotable.insert(tab, a)local t = a + ba = bb = tSleep(1)endreturn tabendlocal fib = Fibonacci(10)for i, v in ipairs(fib) doio.write(v.." ")endfunction Fibonacci(n) local tab = {} local a, b = 1, 1 for i = 1, n do table.insert(tab, a) local t = a + b a = b b = t Sleep(1) end return tab end local fib = Fibonacci(10) for i, v in ipairs(fib) do io.write(v.." ") endfunction Fibonacci(n) local tab = {} local a, b = 1, 1 for i = 1, n do table.insert(tab, a) local t = a + b a = b b = t Sleep(1) end return tab end local fib = Fibonacci(10) for i, v in ipairs(fib) do io.write(v.." ") end
运行结果:1 1 2 3 5 8 13 21 34 55
运行过程:
会先等待10秒运算,然后瞬间打印所有结果。
而计算过程直接卡死10秒显然是不太好的,通过协程可以优化这个问题。
协程计算可以这样写:
local coro = nilfunction Fibonacci(n)local a, b = 1, 1for i = 1, n docoroutine.yield(a) --挂起当前协程,并返回结果local t = a + ba = bb = tSleep(1)endendcoro = coroutine.create(Fibonacci)function GetFibonacciNext()--开始/继续执行一个协程 正常执行:返回true和yield返回值 发生一个未捕获错误:返回false和错误信息local state, value = coroutine.resume(coro, 10)return valueendfor i = 1, 10 doio.write(GetFibonacciNext().." ")endlocal coro = nil function Fibonacci(n) local a, b = 1, 1 for i = 1, n do coroutine.yield(a) --挂起当前协程,并返回结果 local t = a + b a = b b = t Sleep(1) end end coro = coroutine.create(Fibonacci) function GetFibonacciNext() --开始/继续执行一个协程 正常执行:返回true和yield返回值 发生一个未捕获错误:返回false和错误信息 local state, value = coroutine.resume(coro, 10) return value end for i = 1, 10 do io.write(GetFibonacciNext().." ") endlocal coro = nil function Fibonacci(n) local a, b = 1, 1 for i = 1, n do coroutine.yield(a) --挂起当前协程,并返回结果 local t = a + b a = b b = t Sleep(1) end end coro = coroutine.create(Fibonacci) function GetFibonacciNext() --开始/继续执行一个协程 正常执行:返回true和yield返回值 发生一个未捕获错误:返回false和错误信息 local state, value = coroutine.resume(coro, 10) return value end for i = 1, 10 do io.write(GetFibonacciNext().." ") end
运行结果:1 1 2 3 5 8 13 21 34 55
运行过程:
每计算出一个结果(间隔1秒)就会立刻返回并打印该值,直到打印完10个数列项。
这样的运行方式显然会更加友好,即需要用到该数据时再计算。
将协程用作迭代器
上述的遍历结果,还可以改成迭代器的用法:
-- 把协程用作迭代器function GetFibonacciNext(n)local co = coroutine.create(Fibonacci)return function()local state, value = coroutine.resume(co, n)return valueendendfor v in GetFibonacciNext(10) doio.write(v.." ")end-- 把协程用作迭代器 function GetFibonacciNext(n) local co = coroutine.create(Fibonacci) return function() local state, value = coroutine.resume(co, n) return value end end for v in GetFibonacciNext(10) do io.write(v.." ") end-- 把协程用作迭代器 function GetFibonacciNext(n) local co = coroutine.create(Fibonacci) return function() local state, value = coroutine.resume(co, n) return value end end for v in GetFibonacciNext(10) do io.write(v.." ") end
这样写也能达到相同的效果,且像迭代器一样会更方便使用。
而由于GetFibonacciNext这种写法很常见,即将唤醒对应协程的调用包装在一个函数中,所以Lua语言专门为此提供了一个函数coroutine.wrap来完成这个功能。(wrap的介绍详见上表。)
使用wrap函数我们能将GetFibonacciNext函数改成如下代码:
function GetFibonacciNext(n)return coroutine.wrap(function() Fibonacci(n) end)endfunction GetFibonacciNext(n) return coroutine.wrap(function() Fibonacci(n) end) endfunction GetFibonacciNext(n) return coroutine.wrap(function() Fibonacci(n) end) end
效果一致。
© 版权声明
本站文章由不念博客原创,未经允许严禁转载!
THE END