Lua coroutine
What is coroutine?
Lua coroutine is similar to a thread: it has an independent stack, independent local variables, and independent instruction pointer, and at the same time shares global variables and most other things with other coroutines.
Collaboration is a very powerful function, but it is also very complicated to use.
The difference between threads and coroutines
The main difference between threads and coroutines is that a program with multiple threads can run several threads at the same time, while coroutines need to run in cooperation with each other.
Only one coroutine is running at any given time, and the running coroutine will only be suspended when explicitly asked to suspend.
Coroutines are somewhat similar to synchronized multi-threads, and several threads waiting for the same thread lock are somewhat similar to collaboration.
Basic syntax
Method | Description |
---|---|
coroutine.create() | Create coroutine and return coroutine. The parameter is a function. When used with resume, the function call is awakened. |
coroutine.resume() | Restart Coroutine, used in conjunction with create |
coroutine.yield() | Suspend coroutine and set coroutine to suspended state. This can be used in conjunction with resume to be useful. Effect |
coroutine.status() | Check the status of coroutine Note: There are three statuses of coroutine: dead, suspend, and running. When will it happen? For this status, please refer to the following program |
coroutine.wrap() | Create coroutine and return a function. Once you call this function, enter coroutine and create Function duplication |
coroutine.running() | Returns the running coroutine. A coroutine is a thread. When running is used, a corouting thread number is returned. |
The following examples demonstrate the usage of each of the above methods:
-- 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("----------")
The execution output of the above examples is:
1 dead ---------- 1 ---------- 1 2 3 running thread: 0x7fb801c05868 false suspended thread: 0x7fb801c04c88 true ----------
It can be seen from coroutine.running that the underlying implementation of coroutine is a thread.
When creating a coroutine, an event is registered in a new thread.
When resume is used to trigger an event, the coroutine function of create is executed. When yield is encountered, it means suspending the current thread and waiting for resume to trigger the event again.
Next we analyze a more detailed example:
function foo (a) print("foo 函数输出", a) return coroutine.yield(2 * a) -- 返回 2*a 的值 end co = coroutine.create(function (a , b) print("第一次协同程序执行输出", a, b) -- co-body 1 10 local r = foo(a + 1) print("第二次协同程序执行输出", r) local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入 print("第三次协同程序执行输出", r, s) return b, "结束协同程序" -- 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 end print("---分割线---") print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine print("---分割线---")
The execution output of the above example is:
第一次协同程序执行输出 1 10 foo 函数输出 2 main true 4 --分割线---- 第二次协同程序执行输出 r main true 11 -9 ---分割线--- 第三次协同程序执行输出 x y main true 10 结束协同程序 ---分割线--- main false cannot resume dead coroutine ---分割线---
The above example continues as follows:
Call resume to wake up the coroutine. The resume operation returns true if successful, otherwise it returns false;
The coroutine runs;
Run to the yield statement;
yield suspends the coroutine, and the first resume returns; (Note: here yield returns, and the parameters are the parameters of resume)
Resume for the second time, wake up the coroutine again; (Note: Among the resume parameters here, except for the first parameter, the remaining parameters will be used as yield parameters)
yield returns;
The coroutine continues to run;
If the coroutine used continues to run and continues to call the resume method after completion, Output: cannot resume dead coroutine
The powerful combination of resume and yield is that resume is in the main process, and it passes external status (data) into the coroutine; and yield The internal status (data) is returned to the main process.
Producer-Consumer Problem
Now I use Lua's coroutine to complete the classic producer-consumer problem.
local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- 将生产的物品发送给消费者 end end function consumer() while true do local i = receive() -- 从生产者那里得到物品 print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x表示需要发送的值,值返回以后,就挂起该协同程序 end -- 启动程序 newProductor = coroutine.create(productor) consumer()
The execution output of the above example is:
1 2 3 4 5 6 7 8 9 10 11 12 13 ……