English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Lua 협력 프로그램(coroutine)

코루틴(coroutine)이 무엇인가요?

Lua 코루틴(coroutine)은 스레드와 비슷합니다: 독립된 스택, 독립된 지역 변수, 독립된 명령 포인터를 가지고 있으며, 동시에 다른 코루틴과 공유된 전역 변수와 대부분의 다른 것들을 공유합니다.

코루틴은 매우 강력한 기능이지만 사용하기에도 복잡합니다.

스레드와 코루틴의 차이

스레드와 협력 프로그램의 주요 차이는, 여러 스레드를 동시에 실행할 수 있는 프로그램이 있지만, 협력 프로그램은 상호 협력하여 실행되어야 합니다.

어떤 순간에나 하나의 협력 프로그램만이 실행되며, 실행 중인 협력 프로그램은 명확하게 중지되지 않는 한 중지됩니다.

협력 프로그램은 동기적인 멀티 스레드와 유사하며, 동일한 스레드 락을 기다리는 여러 스레드는 협력과 유사합니다.

基本语法

方法描述
coroutine.create()创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用
coroutine.resume()重启 coroutine,和 create 配合使用
coroutine.yield()挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果
coroutine.status()查看 coroutine 的状态
注:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序
coroutine.wrap()创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复
coroutine.running()返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号

以下示例演示了以上各个方法的用法:

-- 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("----------")

위의 예제 실행 결과는 다음과 같습니다:

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

coroutine.running을 사용하면 coroutine가 기본적으로 스레드로 구현되었다는 것을 알 수 있습니다.

coroutine를 create할 때, 새로운 스레드에 이벤트를 등록하는 것입니다.

resume를 사용하여 이벤트를 트리거할 때, create의 coroutine 함수가 실행되고, yield를 만나면 현재 스레드를 일시정지하고 다시 resume를 통해 이벤트를 트리거하는 것을 의미합니다。

다음은 더 상세한 예제를 분석해 보겠습니다:

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("---구분선---")

위의 예제 실행 결과는 다음과 같습니다:

첫 번째协同프로그램 실행 출력    1    10
foo 함수 출력    2
main    true    4
--구분선----
두 번째协同프로그램 실행 출력    r
main    true    11    -9
---구분선---
세 번째协同프로그램 실행 출력    x    y
main    true    10    协同프로그램을 종료합니다
---구분선---
main    false    cannot resume dead coroutine
---구분선---

위 예제는 다음과 같이 계속됩니다:

  • resume 호출하여协同프로그램을 깨우고, resume 작업이 성공하면 true를 반환하며, 실패하면 false를 반환합니다;

  • 协同프로그램이 실행됩니다;

  • yield 문장까지 실행됩니다;

  • yield를 사용한协同프로그램, 첫 번째 resume가 반환됩니다;(주의:이곳에서 yield는 resume의 매개변수를 반환합니다)

  • 두 번째 resume를 통해 협력 프로그램을 다시 깨우기;(주의: 여기서 resume의 매개변수는 첫 번째 매개변수 외에 나머지 매개변수는 yield의 매개변수로 사용됩니다)

  • yield가 반환됩니다;

  • 협력 프로그램이 계속 실행됩니다;

  • 사용 중인 협력 프로그램이 계속 실행되고 complete된 후에 resume 메서드를 다시 호출하면 cannot resume dead coroutine가 출력됩니다

resume와 yield의 강력한 점은 resume가 메인 프로세스에 있어, 외부 상태(데이터)를 협력 프로그램 내부로 전달하고, yield는 내부 상태(데이터)를 메인 프로세스로 반환하는 것입니다

생산자-소비자 문제

이제 Lua의 협력 프로그램을 사용하여 생산자 문제를 해결하겠습니다-소비자 문제

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()

위의 예제 실행 결과는 다음과 같습니다:

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