Lua語言基礎匯總(7) -- 協同程式

來源:互聯網
上載者:User

標籤:style   http   使用   io   strong   資料   for   ar   

前言

協同程式與線程差不多,也就是一條執行序列,擁有自己獨立的棧、局部變數和指令指標,同時又與其它協同程式共用全域變數和其它大部分東西。從概念上講,線程與協同程式的主要區別在於,一個具有多個線程的程式可以同時運行幾個線程,而協同程式卻需要彼此協作的運行。就是說,一個具有多個協同程式的程式在任意時刻只能運行一個協同程式,並且正在啟動並執行協同程式只會在其顯式地要求掛起時,它的執行才會暫停。

 

協同程式基礎

Lua將所有關於協同程式的函數放置在一個名為“coroutine”的table中。函數create用於建立新的協同程式,它只有一個參數,就是一個函數。該函數的代碼就是協同程式需要執行的內容。create會返回一個thread類型的值,用以表示新的協同程式,一般create的參數是一個匿名函數,例如以下代碼:

1 local co = coroutine.create(function () print("Hello WOrld") end)

 

一個協同程式可以有四種不同的狀態:掛起(suspended)、運行(running)、死亡(dead)和正常(normal)。當新建立一個協同程式時,它處於掛起狀態,言外之意就是,協同程式不會在建立它時自動執行其內容,我們可以通過函數status來檢查協同程式的狀態。

12 local co = coroutine.create(function () print("Hello WOrld") end) print(coroutine.status(co))     -- suspended

 

函數coroutine.resume用於啟動或再次啟動一個協同程式的執行,並將其狀態由掛起改為運行:

123 local co = coroutine.create(function () print("Hello WOrld") end) print(coroutine.status(co))     -- suspended coroutine.resume(co)          -- Hello World

 

上面的代碼中,我調用了resume函數,將協同程式co由suspended改為running狀態,當列印了Hello World之後,協同程式co就處於死亡狀態。

 

到目前為止,協同程式就是一種函數調用。其實,協同程式的真正強大之處在於函數yield的使用上,該函數可以讓一個運行中的協同程式掛起,而之後可以再恢複它的運行,例如以下代碼:

123456789101112131415161718192021222324252627282930313233 local co = coroutine.create(function ()      for i = 1, 10 do          print("co", i)           coroutine.yield()      end end)   -- 列印初始狀態 print(coroutine.status(co))     -- suspended   -- 喚醒協同程式co coroutine.resume(co)     -- 列印co 1   -- 列印協同程式的狀態 print(coroutine.status(co))     -- suspended   -- 再次喚醒協同程式co coroutine.resume(co)     -- 列印co 2   -- 列印協同程式的狀態 print(coroutine.status(co))     -- suspended   coroutine.resume(co)     -- 列印co 3 coroutine.resume(co)     -- 列印co 4 coroutine.resume(co)     -- 列印co 5 coroutine.resume(co)     -- 列印co 6 coroutine.resume(co)     -- 列印co 7 coroutine.resume(co)     -- 列印co 8 coroutine.resume(co)     -- 列印co 9 coroutine.resume(co)     -- 列印co 10 coroutine.resume(co)     -- 什麼都不列印 print(coroutine.status(co))     -- dead coroutine.resume(co)

 

當在協同程式的執行中發生任何錯誤,Lua是不會顯示錯誤訊息的,而是將執行權返回給resume調用。當coroutine.resume的第一個傳回值為false時,就表明協同程式在運行過程中發生了錯誤;當值為true時,則表明協同程式運行正常。

 

當一個協同程式A喚醒另一個協同程式B時,協同程式A就處於一個特殊狀態,既不是掛起狀態(無法繼續A的執行),也不是運行狀態(是B在運行)。所以將這時的狀態稱為“正常”狀態。

 

Lua的協同程式還具有一項有用的機制,就是可以通過一對resume-yield來交換資料。在第一次調用resume時,並沒有對應的yield在等待它,因此所有傳遞給resume的額外參數都視為協同程式主函數的參數。如下述代碼:

 

當協同程式中沒有yield時,第一次調用resume,所有傳遞給resume的額外參數都將視為協同程式主函數的參數,如以下代碼:

12345 local co = coroutine.create(function (a, b, c)      print("co", a, b, c) end)   coroutine.resume(co, 1, 2, 3)     -- co 1 2 3

 

 

當協同程式中存在yield時,一切就變的複雜了,先來分析一下這個流程:

1. 調用resume,將協同程式喚醒;

2. 協同程式運行;

3. 運行到yield語句;

4. yield掛起協同程式,第一次resume返回(注意:此處yield返回,參數是resume的參數);

5. 第二次resume,再次喚醒協同程式(注意:此處resume的參數中,除了第一個參數,剩下的參數將作為yield的參數);

6. yield返回;

7. 協同程式繼續運行;

 

此處從其它部落格中借鑒的一部分代碼,可以說明上面的調用流程:

123456789101112131415161718192021222324 function foo (a)     print("foo", a)  -- foo 2     return coroutine.yield(2 * a) -- return 2 * a end   co = coroutine.create(function (a , b)     print("co-body", a, b) -- co-body 1 10     local r = foo(a + 1)       print("co-body2", r)     local r, s = coroutine.yield(a + b, a - b)       print("co-body3", r, s)     return b, "end"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")) -- false cannot resume dead coroutine print("------")

 

輸出結果如下:

1234567891011121314 >lua -e "io.stdout:setvbuf ‘no‘" "test.lua"co-body     1     10 foo     2 main     true     4 ------ co-body2     r main     true     11     -9 ------ co-body3     x     y main     true     10     end ------ main     false     cannot resume dead coroutine ------ >Exit code: 0

 

resume和yield的配合強大之處在於,resume處於主程中,它將外部狀態(資料)傳入到協同程式內部;而yield則將內部的狀態(資料)返回到主程中。

 

生產者-消費者問題

現在我就使用Lua的協同程式來完成生產者-消費者這一經典問題。生產者生產東西,消費者消費生產者生產的東西。

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

 

來源網址:http://www.jellythink.com/archives/508

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.