1. 執(zhí)行過(guò)程
- 每個(gè)線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨(dú)立執(zhí)行,必須依存在進(jìn)程中,由進(jìn)程提供多個(gè)線程執(zhí)行控制。每個(gè)線程都有他自己的一組CPU寄存器,稱為線程的上下文,該上下文反映了線程上次運(yùn)行該線程的CPU寄存器的狀態(tài)。
- 協(xié)程,又稱微線程,Coroutine。執(zhí)行過(guò)程中,在子程序內(nèi)部可中斷,然后轉(zhuǎn)而執(zhí)行別的子程序,在適當(dāng)?shù)臅r(shí)候再返回來(lái)接著執(zhí)行。實(shí)際上就是對(duì)函數(shù)調(diào)用流程的一種控制方式,讓函數(shù)互相協(xié)作配合,這就是協(xié)程。
2. 調(diào)度方式
- 進(jìn)程和線程完全由操作系統(tǒng)負(fù)責(zé)調(diào)度,程序自己不能決定什么時(shí)候執(zhí)行,執(zhí)行多長(zhǎng)時(shí)間。
- 協(xié)程則是在程序中,自己負(fù)責(zé)調(diào)度,更加靈活,但復(fù)雜度較高。
3. 運(yùn)行效率
- 進(jìn)程是重量級(jí)別的程序,創(chuàng)建和銷毀開(kāi)銷大。
- 線程是輕量級(jí)別的程序,相比進(jìn)程下創(chuàng)建和銷毀開(kāi)銷小,切換速度較快。
- 協(xié)程則是單線程的異步編程模型。和多線程比,線程數(shù)量越多,CPU就會(huì)花掉更多時(shí)間在切換中,而沒(méi)有線程切換、保存上下文的開(kāi)銷的協(xié)程,相比下運(yùn)行效率則更高。第二大優(yōu)勢(shì)就是不需要多線程的鎖機(jī)制,因?yàn)橹挥幸粋€(gè)線程,也不存在同時(shí)寫變量沖突,在協(xié)程中控制共享資源不加鎖,所以協(xié)程性能優(yōu)勢(shì)更加明顯。
4. CPU利用
- 線程和協(xié)程由于CPython中全局解釋器鎖GIL的問(wèn)題,只能使用到單核CPU的計(jì)算資源
- 進(jìn)程則可以運(yùn)行多個(gè)(數(shù)量與CPU核心數(shù)相同),充分利用多核CPU
CPython解釋器本身不是線程安全的,因此需要全局解釋器鎖GIL,一次只允許一個(gè)線程執(zhí)行Python字節(jié)碼。因此一個(gè)Python進(jìn)程不能同時(shí)使用到多個(gè)CPU核心。
然而,標(biāo)準(zhǔn)庫(kù)中所有執(zhí)行阻塞型 IO 操作的函數(shù),在等待結(jié)果返回時(shí)都會(huì)釋放GIL。這意味著盡管有GIL,Python線程還是能在 IO 密集型任務(wù)中一展身手。 引用自《流暢的Python》
5. 最佳實(shí)踐
- 線程和協(xié)程推薦在IO密集型的任務(wù)(比如網(wǎng)絡(luò)調(diào)用)中使用,而在CPU密集型的任務(wù)中,表現(xiàn)較差。
- 對(duì)于CPU密集型的任務(wù),則需要多個(gè)進(jìn)程,繞開(kāi)GIL的限制,利用所有可用的CPU核心,提高效率。
- 所以大并發(fā)下的最佳實(shí)踐就是多進(jìn)程+協(xié)程,既充分利用多核,又充分發(fā)揮協(xié)程的高效率,可獲得極高的性能。
順便一提,非常流行的一個(gè)爬蟲(chóng)框架Scrapy就是用到異步框架Twisted來(lái)進(jìn)行任務(wù)的調(diào)度,這也是Scrapy框架高性能的原因之一。