我們首先介紹一下什么是協(xié)程肃晚、然后詳細(xì)介紹一下coroutine庫,然后介紹一下協(xié)程的簡單用法欢策,最后介紹一下協(xié)程的復(fù)雜用法吆寨。
一、協(xié)程是什么踩寇?
(1)線程
首先復(fù)習(xí)一下多線程啄清。我們都知道線程——Thread。每一個(gè)線程都代表一個(gè)執(zhí)行序列俺孙。
當(dāng)我們在程序中創(chuàng)建多線程的時(shí)候辣卒,看起來,同一時(shí)刻多個(gè)線程是同時(shí)執(zhí)行的睛榄,不過實(shí)質(zhì)上多個(gè)線程是并發(fā)的荣茫,因?yàn)橹挥幸粋€(gè)CPU,所以實(shí)質(zhì)上同一個(gè)時(shí)刻只有一個(gè)線程在執(zhí)行懈费。
在一個(gè)時(shí)間片內(nèi)執(zhí)行哪個(gè)線程是不確定的计露,我們可以控制線程的優(yōu)先級,不過真正的線程調(diào)度由CPU的調(diào)度決定憎乙。
(2)協(xié)程
那什么是協(xié)程呢票罐?協(xié)程跟線程都代表一個(gè)執(zhí)行序列。不同的是泞边,協(xié)程把線程中不確定的地方盡可能的去掉该押,執(zhí)行序列間的切換不再由CPU隱藏的進(jìn)行,而是由程序顯式的進(jìn)行阵谚。
所以蚕礼,使用協(xié)程實(shí)現(xiàn)并發(fā),需要多個(gè)協(xié)程彼此協(xié)作梢什。
二奠蹬、resume和yeild的協(xié)作。
resume和yeild的協(xié)作是Lua協(xié)程的核心嗡午。這邊用一幅圖描述一下囤躁,有一個(gè)大體的印象。對照下面的coroutine庫的詳細(xì)解釋和最后的代碼荔睹,應(yīng)該可以搞清楚協(xié)程的概念了狸演。
注:這是在非首次resume協(xié)程的情況下,resume和yield的互相調(diào)用的情況僻他。如果是首次resume協(xié)程宵距,那么resume的參數(shù)會直接傳遞給協(xié)程函數(shù)。
三吨拗、coroutine庫詳解
(1)coroutine.create (f)
傳一個(gè)函數(shù)參數(shù)满哪,用來創(chuàng)建協(xié)程婿斥。返回一個(gè)“thread”對象。
(2)coroutine.isyieldable ()
如果正在運(yùn)行的協(xié)程可以讓出翩瓜,則返回真受扳。值得注意的是,只有主協(xié)程(線程)和C函數(shù)中是無法讓出的兔跌。
(3)coroutine.resume (co [, val1, ···])
這是一個(gè)非常重要的函數(shù)勘高。用來啟動或再次啟動一個(gè)協(xié)程,使其由掛起狀態(tài)變成運(yùn)行狀態(tài)坟桅。
可以這么說华望,resume函數(shù)相當(dāng)于在執(zhí)行協(xié)程中的方法。參數(shù)Val1...是執(zhí)行協(xié)程co時(shí)傳遞給協(xié)程的方法仅乓。
首次執(zhí)行協(xié)程co時(shí)赖舟,參數(shù)Val1...會傳遞給協(xié)程co的函數(shù);
再次執(zhí)行協(xié)程co時(shí)夸楣,參數(shù)Val1...會作為給協(xié)程co中上一次yeild的返回值宾抓。
不知道這句話大家理解了沒,這是協(xié)程的核心豫喧。如果沒理解也不用急石洗,繼續(xù)往下看,稍后我會詳細(xì)解釋紧显。
resume函數(shù)返回什么呢讲衫?有3種情況:
1)、如果協(xié)程co的函數(shù)執(zhí)行完畢孵班,協(xié)程正常終止涉兽,resume返回 true和函數(shù)的返回值。
2)篙程、如果協(xié)程co的函數(shù)執(zhí)行過程中枷畏,協(xié)程讓出了(調(diào)用了yeild()方法),那么resume返回true和協(xié)程中調(diào)用yeild傳入的參數(shù)虱饿。
3)矿辽、如果協(xié)程co的函數(shù)執(zhí)行過程中發(fā)生錯(cuò)誤,resume返回false與錯(cuò)誤消息郭厌。
可以看到resume無論如何都不會導(dǎo)致程序崩潰。它是在保護(hù)模式下執(zhí)行的雕蔽。
(4)coroutine.running ()
用來判斷當(dāng)前執(zhí)行的協(xié)程是不是主線程折柠,如果是,就返回true批狐。
(5)coroutine.status (co)
返回一個(gè)字符串扇售,表示協(xié)程的狀態(tài)前塔。有4種狀態(tài):
1)、running承冰。如果在協(xié)程的函數(shù)中調(diào)用status华弓,傳入?yún)f(xié)程自身的句柄,那么執(zhí)行到這里的時(shí)候才會返回running狀態(tài)困乒。
2)寂屏、suspended。如果協(xié)程還未結(jié)束娜搂,即自身調(diào)用了yeild或還沒開始運(yùn)行迁霎,那么就是suspended狀態(tài)。
3)百宇、normal考廉。如果協(xié)程Aresume協(xié)程B時(shí),協(xié)程A處于的狀態(tài)為normal携御。在協(xié)程B的執(zhí)行過程中昌粤,協(xié)程A就一直處于normal狀態(tài)。因?yàn)樗@時(shí)候既不是掛起狀態(tài)啄刹、也不是運(yùn)行狀態(tài)涮坐。
4)、dead鸵膏。如果一個(gè)協(xié)程發(fā)生錯(cuò)誤結(jié)束膊升,或正常終止。那么就處于dead狀態(tài)谭企。如果這時(shí)候?qū)λ{(diào)用resume廓译,將返回false和錯(cuò)誤消息。
(6)coroutine.wrap (f)
wrap()也是用來創(chuàng)建協(xié)程的债查。只不過這個(gè)協(xié)程的句柄是隱藏的非区。跟create()的區(qū)別在于:
1)、wrap()返回的是一個(gè)函數(shù)盹廷,每次調(diào)用這個(gè)函數(shù)相當(dāng)于調(diào)用coroutine.resume()征绸。
2)、調(diào)用這個(gè)函數(shù)相當(dāng)于在執(zhí)行resume()函數(shù)俄占。
3)管怠、調(diào)用這個(gè)函數(shù)時(shí)傳入的參數(shù),就相當(dāng)于在調(diào)用resume時(shí)傳入的除協(xié)程的句柄外的其他參數(shù)缸榄。
4)渤弛、調(diào)用這個(gè)函數(shù)時(shí),跟resume不同的是甚带,它并不是在保護(hù)模式下執(zhí)行的她肯,若執(zhí)行崩潰會直接向外拋出佳头。
(7)coroutine.yield (···)
使正在執(zhí)行的函數(shù)掛起。
傳遞給yeild的參數(shù)會作為resume的額外返回值晴氨。
同時(shí)康嘉,如果對該協(xié)程不是第一次執(zhí)行resume,resume函數(shù)傳入的參數(shù)將會作為yield的返回值籽前。