English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Lua coroutines (coroutines) are similar to threads: they have their own stack, local variables, and instruction pointer, while also sharing global variables and most other things with other coroutines.
Coroutines are a very powerful feature, but they are also quite complex to use.
The main difference between threads and coroutines is that a program with multiple threads can run multiple threads simultaneously, while coroutines need to run in collaboration with each other.
At any given moment, only one coroutine is running, and the running coroutine can only be suspended when explicitly requested.
Coroutines are somewhat similar to synchronized multithreading, and threads waiting for the same thread lock are somewhat similar to coroutines.
Method | Description |
---|---|
coroutine.create() | Create a coroutine, returns coroutine, the parameter is a function, when used with resume, it wakes up the function call |
coroutine.resume() | Restart coroutine, used with create |
coroutine.yield() | Suspend coroutine, set the coroutine to suspended state, this can be used with resume to achieve many useful effects |
coroutine.status() | Check the status of coroutine Note: The status of coroutine has three: dead, suspended, running, specific when such status occurs, please refer to the following program |
coroutine.wrap() | Create a coroutine, returns a function, once you call this function, you enter the coroutine, which is redundant with the create function |
coroutine.running() | Returns the running coroutine, a coroutine is a thread, when using running, it returns the thread number of the coroutine |
-- coroutine_test.lua file 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 output result of the above example is:
1 dead ---------- 1 ---------- 1 2 3 running thread: 0x7fb801c05868 false suspended thread: 0x7fb801c04c88 true ----------
coroutine.running can be seen, coroutine is implemented as a thread in the underlying implementation.
When creating a coroutine, it is equivalent to registering an event in a new thread.
When using resume to trigger an event, the coroutine function of create is executed, and when encountering yield, it means to suspend the current thread and wait for the next resume to trigger the event.
Next, we analyze a more detailed example:
function foo (a) print("foo function output", a) return coroutine.yield(2 * a) -- Return 2*The value of a end co = coroutine.create(function (a , b) print("The first cooperative program execution output", a, b) -- co-body 1 10 local r = foo(a + 1) print("The second cooperative program execution output", r) local r, s = coroutine.yield(a + b, a - b) -- The values of a, b are the values passed when the cooperative program is called for the first time print("The third cooperative program execution output", r, s) return b, "End the cooperative program" -- The value of b is the value passed when the cooperative program is called for the second time end) print("main", coroutine.resume(co, 1, 10)) -- true, 4 print("--Separator line----") print("main", coroutine.resume(co, "r")) -- true 11 -9 print("---Separator line---") print("main", coroutine.resume(co, "x", "y")) -- true 10 end print("---Separator line---") print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine print("---Separator line---")
The output result of the above example is:
The first cooperative program execution output 1 10 Output of the foo function 2 main true 4 --Separator line---- The second cooperative program execution output r main true 11 -9 ---Separator line--- The third cooperative program execution output x y main true 10 End the cooperative program ---Separator line--- main false cannot resume dead coroutine ---Separator line---
The following example continues:
Call resume to wake up the cooperative program, resume operation returns true if successful, otherwise returns false;
The cooperative program runs;
Run to the yield statement;
Yield the cooperative program, the first resume returns; (Note: The yield returns, the parameter is the resume parameter)
Second resume, wake up the coroutine again; (Note: here, the parameters in the resume parameter, except the first parameter, the remaining parameters will be as the parameters of yield)
yield returns;
Coroutine continues to run;
If the coroutine continues to run after the resume method is called after it is completed, it will output: cannot resume dead coroutine
The powerful combination of resume and yield lies in the fact that resume is in the main program, it passes the external state (data) into the coroutine; while yield returns the internal state (data) back to the main program.
Now I will use Lua's coroutines to complete the producer-This is a classic consumer problem.
local newProductor function productor() local i = 0 while true do i = i + 1 send(i) -- Send the produced item to the consumer end end function consumer() while true do local i = receive() -- Get the item from the producer print(i) end end function receive() local status, value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) -- x represents the value to be sent, and the coroutine is suspended after the value is returned end -- Start the program newProductor = coroutine.create(productor) consumer()
The output result of the above example is:
1 2 3 4 5 6 7 8 9 10 11 12 13 ...