Lua 協(xié)同程序(coroutine)

2022-06-25 15:19 更新

什么是協(xié)同(coroutine)?

Lua 協(xié)同程序(coroutine)與線程比較類似:擁有獨(dú)立的堆棧,獨(dú)立的局部變量,獨(dú)立的指令指針,同時又與其它協(xié)同程序共享全局變量和其它大部分東西。

協(xié)同是非常強(qiáng)大的功能,但是用起來也很復(fù)雜。

線程和協(xié)同程序區(qū)別

線程與協(xié)同程序的主要區(qū)別在于,一個具有多個線程的程序可以同時運(yùn)行幾個線程,而協(xié)同程序卻需要彼此協(xié)作的運(yùn)行。

在任一指定時刻只有一個協(xié)同程序在運(yùn)行,并且這個正在運(yùn)行的協(xié)同程序只有在明確的被要求掛起的時候才會被掛起。

協(xié)同程序有點(diǎn)類似同步的多線程,在等待同一個線程鎖的幾個線程有點(diǎn)類似協(xié)同。

基本語法

方法 描述
coroutine.create() 創(chuàng)建coroutine,返回coroutine, 參數(shù)是一個函數(shù),當(dāng)和resume配合使用的時候就喚醒函數(shù)調(diào)用
coroutine.resume() 重啟coroutine,和create配合使用
coroutine.yield() 掛起coroutine,將coroutine設(shè)置為掛起狀態(tài),這個和resume配合使用能有很多有用的效果
coroutine.status() 查看coroutine的狀態(tài)
注:coroutine的狀態(tài)有三種:dead,suspend,running,具體什么時候有這樣的狀態(tài)請參考下面的程序
coroutine.wrap() 創(chuàng)建coroutine,返回一個函數(shù),一旦你調(diào)用這個函數(shù),就進(jìn)入coroutine,和create功能重復(fù)
coroutine.running() 返回正在跑的coroutine,一個coroutine就是一個線程,當(dāng)使用running的時候,就是返回一個corouting的線程號

以下實(shí)例演示了以上各個方法的用法:

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

以上實(shí)例執(zhí)行輸出結(jié)果為:

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

coroutine.running就可以看出來,coroutine在底層實(shí)現(xiàn)就是一個線程。

當(dāng)create一個coroutine的時候就是在新線程中注冊了一個事件。

當(dāng)使用resume觸發(fā)事件的時候,create的coroutine函數(shù)就被執(zhí)行了,當(dāng)遇到y(tǒng)ield的時候就代表掛起當(dāng)前線程,等候再次resume觸發(fā)事件。

接下來我們分析一個更詳細(xì)的實(shí)例:

function foo (a)
    print("foo 函數(shù)輸出", a)
    return coroutine.yield(2 * a) -- 返回  2*a 的值
end
 
co = coroutine.create(function (a , b)
    print("第一次協(xié)同程序執(zhí)行輸出", a, b) -- co-body 1 10
    local r = foo(a + 1)
     
    print("第二次協(xié)同程序執(zhí)行輸出", r)
    local r, s = coroutine.yield(a + b, a - b)  -- a,b的值為第一次調(diào)用協(xié)同程序時傳入
     
    print("第三次協(xié)同程序執(zhí)行輸出", r, s)
    return b, "結(jié)束協(xié)同程序"                   -- b的值為第二次調(diào)用協(xié)同程序時傳入
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("---分割線---")

以上實(shí)例執(zhí)行輸出結(jié)果為:

第一次協(xié)同程序執(zhí)行輸出 1   10
foo 函數(shù)輸出    2
main true    4
--分割線----
第二次協(xié)同程序執(zhí)行輸出   r
main true    11  -9
---分割線---
第三次協(xié)同程序執(zhí)行輸出  x   y
main true    10  結(jié)束協(xié)同程序
---分割線---
main false   cannot resume dead coroutine
---分割線---

以上實(shí)例接下如下:

  • 調(diào)用resume,將協(xié)同程序喚醒,resume操作成功返回true,否則返回false;
  • 協(xié)同程序運(yùn)行;
  • 運(yùn)行到y(tǒng)ield語句;
  • yield掛起協(xié)同程序,第一次resume返回;(注意:此處yield返回,參數(shù)是resume的參數(shù))
  • 第二次resume,再次喚醒協(xié)同程序;(注意:此處resume的參數(shù)中,除了第一個參數(shù),剩下的參數(shù)將作為yield的參數(shù))
  • yield返回;
  • 協(xié)同程序繼續(xù)運(yùn)行;
  • 如果使用的協(xié)同程序繼續(xù)運(yùn)行完成后繼續(xù)調(diào)用 resumev方法則輸出:cannot resume dead coroutine

resume和yield的配合強(qiáng)大之處在于,resume處于主程中,它將外部狀態(tài)(數(shù)據(jù))傳入到協(xié)同程序內(nèi)部;而yield則將內(nèi)部的狀態(tài)(數(shù)據(jù))返回到主程中。


生產(chǎn)者-消費(fèi)者問題

現(xiàn)在我就使用Lua的協(xié)同程序來完成生產(chǎn)者-消費(fèi)者這一經(jīng)典問題。

local newProductor

function productor()
     local i = 0
     while true do
          i = i + 1
          send(i)     -- 將生產(chǎn)的物品發(fā)送給消費(fèi)者
     end
end

function consumer()
     while true do
          local i = receive()     -- 從生產(chǎn)者那里得到物品
          print(i)
     end
end

function receive()
     local status, value = coroutine.resume(newProductor)
     return value
end

function send(x)
     coroutine.yield(x)     -- x表示需要發(fā)送的值,值返回以后,就掛起該協(xié)同程序
end

-- 啟動程序
newProductor = coroutine.create(productor)
consumer()

以上實(shí)例執(zhí)行輸出結(jié)果為:

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


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號