Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Lua Synergy (coroutine)


May 12, 2021 Lua


Table of contents


Lua Synergy (coroutine)


What is co-operation?

The Lua co-program (coroutine) is similar to threads in that it has a separate stack, separate local variables, independent instruction pointers, and shares global variables and most of other things with other co-programs.

Synergy is a very powerful feature, but it's also complicated to use.

The difference between threads and co-programs

The main difference between a thread and a collaborating program is that a program with multiple threads can run several threads at the same time, while a co-program needs to work with each other.

Only one co-program is running at any one specified time, and the running co-program is suspended only if explicitly required to suspend.

The co-worker is a bit like a synchronized multithreaded, and a few threads waiting for the same thread lock are a bit like a collaboration.

Basic syntax

Method Describe
coroutine.create() Create coroutine and return coroutine, the argument is a function that wakes up when used in conjunction with resume
coroutine.resume() Restart the coroutine and use it with create
coroutine.yield() Suspend the coroutine and set the coroutine to the pending state, which works with resume to have a lot of useful effects
coroutine.status() View the status of coroutine
Note: There are three states of coroutine: dead, suspend, running, and when this is the case, please refer to the following program
coroutine.wrap() Create a coroutine, return a function, and once you call it, you go into the coroutine, and the create function repeats
coroutine.running() Return to the running coroutine, a coroutine is a thread, when using running, is to return a corouting thread number

The following example demonstrates the use of each of these 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())   --nil
 
print("----------")

The output of the above examples is:

1
dead
----------
1
----------
1
2
3
running
thread: 0x7fb801c05868    false
suspended
thread: 0x7fb801c04c88    true
----------

As can be seen from coroutine.running, the coroutine implementation at the bottom is a thread.

When create a coroutine is registered in a new thread, an event is registered.

When a resume is used to trigger an event, the create's coroutine function is executed, and when yield is encountered, it suspends the current thread, waiting for the resume to trigger the event again.

Let's then 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 output of the above examples 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 examples are followed by the following:

  • Call resume, wake up the co-program, resume operation successfully return true, otherwise return false;
  • Co-program operation;
  • Run to yield statement;
  • yield suspends the co-program, the first resume returns; (Note: here yield returns, the parameter is the resume parameter)
  • The second resume wakes up the co-program again; (Note: In the parameters of resume here, the remaining parameters will be used as arguments for yield, except for the first one)
  • yield return;
  • The collaborative program continues to operate;
  • If you continue to call the resumev method after the use of the co-program continues to run, the output: can resume dead coroutine

The powerful fit between resume and yield is that resume is in the main course, which brings the external state (data) into the collaboration program, and yield returns the internal state (data) to the master.


Producer-consumer issues

Now I'm using Lua's collaborative program to solve 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()
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 output of the above examples is:

1
2
3
4
5
6
7
8
9
10
11
12
13
……