一,lua協程簡介 html
協程(coroutine),意思就是協做的例程,最先由Melvin Conway在1963年提出並實現。跟主流程序語言中的線程不同,線程屬於侵入式組件,線程實現的系統稱之爲搶佔式多任務系統,而協程實現的多任務系統成爲協做式多任務系統。線程因爲缺少yield語義,因此運行過程當中不可避免須要調度,休眠掛起,上下文切換等系統開銷,還須要當心使用同步機制保證多線程正常運行。而協程的運行指令系列是固定的,不須要同步機制,協程之間切換也只涉及到控制權的交換,相比較線程來講是很是輕便的。不過同一時刻能夠有多個線程運行,但卻只能有一個協程運行。 git
協程具備兩個很是重要的特性: github
1. 私有數據在協程的間斷式運行期間一直有效 shell
2. 協程每次yield後讓出控制權,下次被resume後從中止點開始繼續執行 編程
通俗的說,比較像一個帶有靜態數據並且具備多個進入點和返回點的函數,下面經過一個簡單例子看看這個性質:數據結構
co = coroutine.create( function(a) print("a = "..a) c = coroutine.yield(a+1) print("c = "..c) return c*2 end ) _, b = coroutine.resume(co, 1) print("b = "..b) _, d = coroutine.resume(co, b ) print("d = "..d)
運行結果:多線程
a = 1 函數
b = 2 lua
c = 2 spa
d = 4
協程co中除了通常函數具備的進入點(函數入口處)和返回點(函數結尾),在yield出還有一個進入點和返回點。主程序第一次resume時將1傳給參數a,運行到yield時,協程返回控制權給主程序,並經過yield的參數提供返回值,因而b=a+1,再次resume時,主程序經過參數b將數據傳給co的變量c,從而從第二個入口點進入函數,最後返回值c*2傳給d。
resume/yield語義實現的協程屬於非對稱協程,在非對稱協程中,調用者和被調用者的關係是固定的,調用者經過resume將控制流轉到被調用者,被調用者經過yield只能返回到調用者,而不能返回到其餘協程。好比A resume B resume C, C yield只能到B,而不能到A或其餘協程。
世間萬物都是對立又統一的,既然存在非對稱協程,固然就存在對稱協程。對稱協程只有一個語義能夠將控制流直接轉到目的協程。
非對稱協程和對稱協程的表達能力是同樣的,lua中只實現了非對稱協程,一個重要緣由是lua是c實現的,非對稱協程的調用與被調用關係與c的函數調用很是相似,方便lua和c的擴展編程。
二,協程實戰
下面例子利用協程實現經典的生產者-消費者模型。
count = 10 productor = coroutine.create( function () i = 0 while(true) do i = i+1 coroutine.yield(i) end end ) consumer = coroutine.create( function(co) n = 1 while(n<count) do _, v = coroutine.resume(co) print(v) n = n+1 end end ) coroutine.resume(consumer, productor)
consumer啓動productor,productor產生一個item後經過yield傳回給consumer,而後掛起等待consumer下次resume,很是簡單直觀。
協程的另外一個重要做用是做爲迭代器,依次訪問數據結構的元素。下面代碼展現了先序訪問二叉樹的方法。
l = { v = 1, left = nil, right = nil, } r = { v = 2, left = nil, right = nil, } root = { v = 3, left = l, right = r, } preorder = function(root) if(root == nil) then return end coroutine.yield(root.v) preorder(root.left) preorder(root.right) end preco = coroutine.create(preorder) view = function(co, root) state, v = coroutine.resume(co, root) if(state == false) then return end print(v) while(true) do state, v = coroutine.resume(co) if(state == false or v == nil) then return end print(v) end end view(preco, root)
對於coroutine, 這裏介紹了一個很是輕量級協程的實現原理,雲風經過uconext實現了相似於lua的協程庫,有興趣的同窗能夠研究研究。
reference:
《coroutines in C》