最近更新比較少,內(nèi)心十分的愧疚,實在是太忙了!向各位讀者說句抱歉。
今天要講的這個東西說實話,我也是今天才知道,一個我們大多數(shù)人可能從來都沒用過的語法,哪就是傳說中的【協(xié)程 Coroutine】。
可能你會說,攜程誰不知道啊,不就是哪個用來訂機(jī)票訂酒店的軟件么,這有什么好學(xué)的!這樣的話你就錯了,此協(xié)程非彼攜程,可不要傻傻分不清楚嘍!
什么是進(jìn)程和線程
進(jìn)程是什么
直白地講,進(jìn)程就是應(yīng)用程序的啟動實例。比如我們運(yùn)行一個游戲,打開一個軟件,就是開啟了一個進(jìn)程,進(jìn)程擁有代碼和打開的文件資源、數(shù)據(jù)資源、獨(dú)立的內(nèi)存空間。
線程又是什么
線程從屬于進(jìn)程,是程序的實際執(zhí)行者。一個進(jìn)程至少包含一個主線程,也可以有更多的子線程,線程擁有自己的??臻g。
線程具有五種狀態(tài):
對操作系統(tǒng)來說,線程是最小的執(zhí)行單元,進(jìn)程是最小的資源管理單元。
無論進(jìn)程還是線程,都是由操作系統(tǒng)所管理的。
進(jìn)程和線程的痛點
線程之間是如何進(jìn)行協(xié)作的呢?
最經(jīng)典的例子就是生產(chǎn)者/消費(fèi)者模式:
若干個生產(chǎn)者線程向隊列中寫入數(shù)據(jù),若干個消費(fèi)者線程從隊列中消費(fèi)數(shù)據(jù)。
什么是協(xié)程
官方定義如下:
A coroutine is a function that can suspend its execution (yield) until the
given givenYieldInstruction finishes.
用我蹩腳的英語來翻譯一下就是:
協(xié)程是一種可以暫停執(zhí)行過程的函數(shù),它可以中斷當(dāng)前的執(zhí)行過程直到下一個 Yield 指令達(dá)成。
我的理解是可以把它當(dāng)成為類似 于CPU 在多個進(jìn)程間切換,從而達(dá)到多個進(jìn)程同時執(zhí)行的效果。協(xié)程
是一種比線程更加輕量級的存在,一個進(jìn)程可以擁有多個線程,一個線程也可以擁有多個協(xié)程。
學(xué)過計算機(jī)組成原理的都知道,當(dāng) CPU 在多個進(jìn)程間切換時,那些后臺程序就會處于這種暫停用英文的 Suspend
或許更恰當(dāng))的狀態(tài),所以早年的電腦即使用一個 CPU 也可以同時處理多個進(jìn)程任務(wù),這是一種“偽多線程”的技術(shù)。
除此之外比較重要的一點是,協(xié)程不是被操作系統(tǒng)內(nèi)核所管理,而完全是由程序所控制(也就是在用戶態(tài)執(zhí)行)。這樣帶來的好處就是性能得到了很大的提升,不會像線程那樣需要上下文切換來消耗資源,因此
協(xié)程的開銷遠(yuǎn)遠(yuǎn)小于線程的開銷。
注意,這里要劃一個重點,協(xié)程是一種“偽多線程”,始終記得這一點,可以幫助我們來理解協(xié)程會這個概念。
協(xié)程函數(shù)的寫法
Java 語言并沒有對協(xié)程提供原生支持,所以用 Java 暫時還演示不了,但是有個開源框架基本模擬除了協(xié)程的功能,感興趣的朋友可以去看看源碼。。。
地址 :https://github.com/kilim/kilim <https://github.com/kilim/kilim>
Go 語言根據(jù)我查詢資料來看,對于協(xié)程的支持超乎我的想象,可以說是強(qiáng)大而簡潔,輕輕松松分分鐘創(chuàng)建成百上千個協(xié)程并發(fā)執(zhí)行。
func Add(x, y int) { z := x + y fmt.Println(z) } func main() { for i:=0; i<10;
i++ { go Add(i, i) } }
如上代碼,在一個函數(shù)調(diào)用前加上 go
關(guān)鍵字,這次調(diào)用就會在一個新的協(xié)程中并發(fā)執(zhí)行。當(dāng)被調(diào)用的函數(shù)返回時,這個協(xié)程也自動結(jié)束。需要注意的是,如果這個函數(shù)有返回值,那么這個返回值會被丟棄。
Python 語言也可以通過 yield/send 的方式實現(xiàn)協(xié)程。在 python 3.5 以后,async/await 成為了更好的替代方案。
def consume(): while True: # consumer 協(xié)程等待接收數(shù)據(jù) number = yield
print("開始消費(fèi)",number) consumer = consume() # 讓初始化狀態(tài)的 consumer 協(xié)程先執(zhí)行起來,在 yield
處停止 next(consumer) for num in range(0,100) print("開始生產(chǎn)",num) # 發(fā)送數(shù)據(jù)給 consumer
協(xié)程 consumer.send(num)
其他語言的寫法我也就不寫了,畢竟不太熟,寫了怕誤人子弟?。?!
總結(jié)
根據(jù)今天查閱的資料來看,協(xié)程的應(yīng)用場景主要在于 :I/O 密集型任務(wù)。
這一點與多線程有些類似,但協(xié)程調(diào)用是在一個線程內(nèi)進(jìn)行的,是單線程,切換的開銷小,因此效率上略高于多線程。當(dāng)程序在執(zhí)行 I/O 時操作時,CPU
是空閑的,此時可以充分利用 CPU 的時間片來處理其他任務(wù)。在單線程中,一個函數(shù)調(diào)用,一般是從函數(shù)的第一行代碼開始執(zhí)行,結(jié)束于 return
語句、異?;蛘吆瘮?shù)執(zhí)行(也可以認(rèn)為是隱式地返回了 None )。 有了協(xié)程,我們在函數(shù)的執(zhí)行過程中,如果遇到了耗時的 I/O 操作,函數(shù)可以臨時讓出控制權(quán),讓
CPU 執(zhí)行其他函數(shù),等 I/O 操作執(zhí)行完畢以后再收回控制權(quán)。
簡單來講協(xié)程的好處:
* 跨平臺
* 跨體系架構(gòu)
* 無需線程上下文切換的開銷
* 無需原子操作鎖定及同步的開銷
* 方便切換控制流,簡化編程模型
* 高并發(fā)+高擴(kuò)展性+低成本:一個CPU支持上萬的協(xié)程都不是問題。所以很適合用于高并發(fā)處理。
缺點:
* 無法利用多核資源:協(xié)程的本質(zhì)是個單線程,它不能同時將 單個CPU
的多個核用上,協(xié)程需要和進(jìn)程配合才能運(yùn)行在多CPU上.當(dāng)然我們?nèi)粘K帉懙慕^大部分應(yīng)用都沒有這個必要,除非是cpu密集型應(yīng)用。
* 進(jìn)行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序:這一點和事件驅(qū)動一樣,可以使用異步IO操作來解決
最后再貼個圖來總結(jié)一下,更清楚:
本文首發(fā)于微信公眾號 【程序猿雜貨鋪】,關(guān)注公眾號,獲取更多精彩文章!
熱門工具 換一換