1. 程式人生 > >多工場景下單執行緒非同步多執行緒多程序

多工場景下單執行緒非同步多執行緒多程序

多工的場景:1.爬取不同url的內容,爬取同一個url分頁內容。比如:豆瓣圖書 Top 250 https://book.douban.com/top250?start=0
實現豆瓣圖書Top250的抓取工作,並存入excel中,如果採用的序列爬取方式,每次爬完250頁都需要花費7到8分鐘,顯然讓人受不了,所以必須在效率上有所提升才行。
仔細想想就可以發現,其實爬10頁(每頁25本),這10頁爬的先後關係是無所謂的,因為寫入的時候沒有依賴關係,各寫各的,所以用序列方式爬取是吃虧的。
顯然可以用併發來加快速度,而且由於沒有同步互斥關係,所以連鎖都不用上,到底應該選擇哪種方式,我們需要先了解各自的優缺點。

本人還有一篇文章是關於

同步與非同步以及執行緒與程序

一、多執行緒任務和多程序任務優缺點
首先,要實現多工,通常我們會設計Master-Worker模式,Master負責分配任務,Worker負責執行任務,因此,多工環境下,通常是一個Master,多個Worker。
如果用多程序實現Master-Worker,主程序就是Master,其他程序就是Worker。
如果用多執行緒實現Master-Worker,主執行緒就是Master,其他執行緒就是Worker。
多程序模式最大的優點就是穩定性高,因為一個子程序崩潰了,不會影響主程序和其他子程序。(當然主程序掛了所有程序就全掛了,但是Master程序只負責分配任務,掛掉的概率低)著名的Apache最早就是採用多程序模式。
多程序模式的缺點是建立程序的代價大,在Unix/Linux系統下,用fork呼叫還行,在Windows下建立程序開銷巨大。另外,作業系統能同時執行的程序數也是有限的,在記憶體和CPU的限制下,如果有幾千個程序同時執行,作業系統連排程都會成問題。
多執行緒模式通常比多程序快一點,但是也快不到哪去,而且,多執行緒模式致命的缺點就是任何一個執行緒掛掉都可能直接造成整個程序崩潰,因為所有執行緒共享程序的記憶體。在Windows上,如果一個執行緒執行的程式碼出了問題,你經常可以看到這樣的提示:“該程式執行了非法操作,即將關閉”,其實往往是某個執行緒出了問題,但是作業系統會強制結束整個程序。
在Windows下,多執行緒的效率比多程序要高,所以微軟的IIS伺服器預設採用多執行緒模式。由於多執行緒存在穩定性的問題,IIS的穩定性就不如Apache。為了緩解這個問題,IIS和Apache現在又有多程序+多執行緒的混合模式,真是把問題越搞越複雜。

二、計算密集型 vs. IO密集型
是否採用多工的第二個考慮是任務的型別。我們可以把任務分為計算密集型和IO密集型。
計算密集型任務的特點是要進行大量的計算,消耗CPU資源,比如計算圓周率、對視訊進行高清解碼等等,全靠CPU的運算能力。這種計算密集型任務雖然也可以用多工完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等於CPU的核心數。
計算密集型任務由於主要消耗CPU資源,因此,程式碼執行效率至關重要。Python這樣的指令碼語言執行效率很低,完全不適合計算密集型任務。對於計算密集型任務,最好用C語言編寫。
第二種任務的型別是IO密集型,涉及到網路、磁碟IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和記憶體的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。常見的大部分任務都是IO密集型任務,比如Web應用。
IO密集型任務執行期間,99%的時間都花在IO上,花在CPU上的時間很少,因此,用執行速度極快的C語言替換用Python這樣執行速度極低的指令碼語言,完全無法提升執行效率。對於IO密集型任務,最合適的語言就是開發效率最高(程式碼量最少)的語言,指令碼語言是首選,C語言最差。

三、非同步IO
考慮到CPU和IO之間巨大的速度差異,一個任務在執行的過程中大部分時間都在等待IO操作,單程序單執行緒模型會導致別的任務無法並行執行,因此,我們才需要多程序模型或者多執行緒模型來支援多工併發執行。
現代作業系統對IO操作已經做了巨大的改進,最大的特點就是支援非同步IO。如果充分利用作業系統提供的非同步IO支援,就可以用單程序單執行緒模型來執行多工,這種全新的模型稱為事件驅動模型,Nginx就是支援非同步IO的Web伺服器,它在單核CPU上採用單程序模型就可以高效地支援多工。在多核CPU上,可以執行多個程序(數量與CPU核心數相同),充分利用多核CPU。由於系統總的程序數量十分有限,因此作業系統排程非常高效。用非同步IO程式設計模型來實現多工是一個主要的趨勢。
對應到Python語言,單執行緒的非同步程式設計模型稱為協程,有了協程的支援,就可以基於事件驅動編寫高效的多工程式。
在學習非同步IO模型前,我們先來了解協程。
協程,又稱微執行緒,纖程。英文名Coroutine。
協程的概念很早就提出來了,但直到最近幾年才在某些語言(如Lua)中得到廣泛應用。
子程式,或者稱為函式,在所有語言中都是層級呼叫,比如A呼叫B,B在執行過程中又呼叫了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。
所以子程式呼叫是通過棧實現的,一個執行緒就是執行一個子程式。
子程式呼叫總是一個入口,一次返回,呼叫順序是明確的。而協程的呼叫和子程式不同。
協程看上去也是子程式,但執行過程中,在子程式內部可中斷,然後轉而執行別的子程式,在適當的時候再返回來接著執行。
注意,在一個子程式中中斷,去執行其他子程式,不是函式呼叫,有點類似CPU的中斷。
協程的特點在於是一個執行緒執行,那和多執行緒比,協程有何優勢?
最大的優勢就是協程極高的執行效率。因為子程式切換不是執行緒切換,而是由程式自身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯。
第二大優勢就是不需要多執行緒的鎖機制,因為只有一個執行緒,也不存在同時寫變數衝突,在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多執行緒高很多。
因為協程是一個執行緒執行,那怎麼利用多核CPU呢?最簡單的方法是多程序+協程,既充分利用多核,又充分發揮協程的高效率,可獲得極高的效能。
Python對協程的支援是通過generator實現的。

四、分散式程序
在Thread和Process中,應當優選Process,因為Process更穩定,而且,Process可以分佈到多臺機器上,而Thread最多隻能分佈到同一臺機器的多個CPU上。
Python的multiprocessing模組不但支援多程序,其中managers子模組還支援把多程序分佈到多臺機器上。一個服務程序可以作為排程者,將任務分佈到其他多個程序中,依靠網路通訊。
由於managers模組封裝很好,不必瞭解網路通訊的細節,就可以很容易地編寫分散式多程序程式。

有刪改,轉載自:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319292979766bd3285c9d6b4942a8ea9b4e9cfb48d8000

下一篇文章舉例說明一下單執行緒非同步、多執行緒、多程序的簡單場景,並對比執行效率