并發(fā)是現(xiàn)實(shí)世界的本質(zhì)特征芦昔,而聰明的計(jì)算機(jī)科學(xué)家用來模擬并發(fā)的技術(shù)手段便是多任務(wù)機(jī)制卵慰。多任務(wù)機(jī)制大致可劃分為兩種:
- 搶占式多任務(wù)(preemptive multi tasking)
搶占式多任務(wù)讓操作系統(tǒng)來決定合適執(zhí)行哪個(gè)任務(wù) - 協(xié)作式多任務(wù)(cooperative multi tasking)
協(xié)作式多任務(wù)將決定權(quán)交給任務(wù)市袖,讓它們?cè)谧约喝蝿?wù)合適的時(shí)候自愿放棄執(zhí)行。
這兩種度偶任務(wù)各有優(yōu)缺掰盘,搶占式多任務(wù)固有的同步問題使得程序經(jīng)常出現(xiàn)不可預(yù)知的行為涛目,協(xié)作式多任務(wù)則要求任務(wù)具備相當(dāng)?shù)淖月删瘛?/p>
協(xié)程(Coroutine)技術(shù)是一種程序控制機(jī)制,早在上世紀(jì)60年代已被提出蝙斜,用它可以很方便地實(shí)現(xiàn)協(xié)作式多任務(wù)名惩。在主流的編程語言如C++、Java孕荠、Pascal...中娩鹉,很少能看見協(xié)程的身影。但在不少動(dòng)態(tài)腳本語言如Python稚伍、Perl...中卻都提供了協(xié)程或與之相似的機(jī)制弯予,其中最為突出便是Lua。
協(xié)程(Coroutine)是指協(xié)作的例程个曙,是多任務(wù)機(jī)制锈嫩。最早由Melvin Conway于1963年提出并實(shí)現(xiàn)受楼。跟主流程序語言中的線程不一樣,線程是屬于侵入式的組件呼寸,線程實(shí)現(xiàn)的系統(tǒng)稱為搶占式多任務(wù)系統(tǒng)艳汽,而協(xié)程實(shí)現(xiàn)的多任務(wù)系統(tǒng)稱為協(xié)作式多任務(wù)系統(tǒng)。
協(xié)程(Coroutine)擁有4種狀態(tài):正常(normal)对雪、掛起(suspended)河狐、運(yùn)行(running)、停止(dead)
- 掛起:協(xié)程創(chuàng)建后或
yield
之后即掛起狀態(tài)瑟捣,此時(shí)不能自動(dòng)運(yùn)行馋艺。 - 運(yùn)行:如果在協(xié)程函數(shù)中調(diào)用
status
,傳入?yún)f(xié)程自身句柄迈套,那么執(zhí)行到這里的時(shí)候會(huì)返回running
的狀態(tài)捐祠。 - 停止:函數(shù)處理完畢后的狀態(tài),此時(shí)不能再重新
resume
桑李。
由于線程缺乏yield
語義踱蛀,所以運(yùn)行過程中不可避免的需要調(diào)度、休眠掛起贵白、上下文切換等系統(tǒng)開銷星岗,而且還需要小心的使用同步機(jī)制保證多線程的正常運(yùn)行。而協(xié)程(Coroutine)的運(yùn)行指令是固定的戒洼,無需同步機(jī)制。協(xié)程之間切換也只涉及到控制權(quán)的交換允华,相比較線程來所是非常輕便的圈浇。不過同一時(shí)刻可以有多個(gè)線程運(yùn)行,但僅能有一個(gè)協(xié)程運(yùn)行靴寂。
Lua語言實(shí)現(xiàn)的協(xié)程(Coroutine)是一種非對(duì)稱式(asymmetric)協(xié)程磷蜀,或稱之為半對(duì)稱式(semi-symmetric)協(xié)程,又或叫半?yún)f(xié)程(semi-coroutine)百炬。這種協(xié)程機(jī)制之所以稱為非對(duì)稱褐隆,是因?yàn)樗峁┝藘煞N傳遞程序控制權(quán)的操作。
- (重)調(diào)用協(xié)程剖踊,即通過
coroutine.resume
實(shí)現(xiàn)庶弃。 - 掛起協(xié)程并將程序控制權(quán)返回給協(xié)程的調(diào)用者,即通過
coroutine.yield
實(shí)現(xiàn)德澈。
一個(gè)半對(duì)稱協(xié)程可以看作是從屬于它的調(diào)用者的歇攻,二者的關(guān)系非常類似于例程(routine)與其調(diào)用者之間的關(guān)系。
既然有非對(duì)稱式協(xié)程梆造,當(dāng)然也就有對(duì)稱式協(xié)程(symmetric)缴守。對(duì)稱式協(xié)程的特點(diǎn)是只有一種傳遞程序控制權(quán)的操作,即將控制權(quán)直接傳遞給指定的協(xié)程。
Lua中的協(xié)程(Coroutine屡穗,協(xié)同程序贴捡,協(xié)同式多線程)和多線程(Thread)和相似,每個(gè)協(xié)程都有自己的堆棧村砂、局部變量烂斋、PC計(jì)數(shù)器,同時(shí)又與其他協(xié)程共享全局變量等箍镜,并通過yield-resume
實(shí)現(xiàn)協(xié)程間的切換源祈。不同之處在于Lua的協(xié)程是非搶占式的多線程,也就是說色迂,你必須手動(dòng)的在協(xié)程間進(jìn)行切換香缺,而且同一時(shí)刻只能有一個(gè)協(xié)程在運(yùn)行。另外歇僧,Lua中的協(xié)程無法在外部將其停止图张,而且是有可能導(dǎo)致程序堵塞的。
相關(guān)概念
進(jìn)程是正在運(yùn)行的程序的實(shí)例
線程(Thread)是進(jìn)程內(nèi)一個(gè)相對(duì)獨(dú)立的诈悍、可調(diào)度的執(zhí)行單元祸轮,是系統(tǒng)獨(dú)立調(diào)度和分配CPU的基本單位,是運(yùn)行中的程序的調(diào)度單位。
每個(gè)線程代表一個(gè)執(zhí)行序列斜做,當(dāng)在程序中創(chuàng)建多線程的時(shí)候堪侯,同一時(shí)刻多個(gè)線程是同時(shí)執(zhí)行的,不過實(shí)質(zhì)上多個(gè)線程是并發(fā)的苦酱,因?yàn)橹挥幸粋€(gè)CPU,所以同一個(gè)時(shí)刻只會(huì)有一個(gè)線程在執(zhí)行给猾。在一個(gè)時(shí)間片內(nèi)哪個(gè)線程執(zhí)行時(shí)不確定的疫萤,可通過控制線程的優(yōu)先級(jí),不過真正的線程調(diào)度是由CPU來決定的敢伸。協(xié)程是指協(xié)作程序扯饶,協(xié)程之間通過協(xié)作(函數(shù)調(diào)用)來完成一個(gè)既定的任務(wù)。
協(xié)程是把線程中不確定的地方盡可能地去掉池颈,執(zhí)行序列間的切換不再由CPU隱藏的進(jìn)行尾序,而是由程序顯式地進(jìn)行。所以躯砰,使用協(xié)程實(shí)現(xiàn)并發(fā)蹲诀,是需要多個(gè)協(xié)程彼此協(xié)作的。
協(xié)程(Coroutine)具有兩個(gè)非常重要的特性:
- 私有數(shù)據(jù)在協(xié)程間斷式運(yùn)行期間一直有效
- 協(xié)程每次
yield
后讓出控制權(quán)弃揽,下次被resume
后從停止點(diǎn)開始繼續(xù)執(zhí)行脯爪。
通俗來說则北,類似一個(gè)帶有靜態(tài)數(shù)據(jù)且具有多個(gè)進(jìn)入點(diǎn)和返回點(diǎn)的函數(shù)。
協(xié)程函數(shù)
- coroutine.create()
協(xié)程的創(chuàng)建痕慢,傳入函數(shù)參數(shù)尚揣,返回thread
線程對(duì)象。 - coroutine.yield()
協(xié)程的暫停掖举,使正在執(zhí)行中的函數(shù)掛起快骗。 - coroutine.resume()
協(xié)程的調(diào)用,用于啟動(dòng)或再次啟動(dòng)一個(gè)協(xié)程塔次,使其由掛起狀態(tài)轉(zhuǎn)變?yōu)檫\(yùn)行狀態(tài)方篮。