1. 程式人生 > >python 程序、執行緒與協程的區別

python 程序、執行緒與協程的區別

簡單總結

程序是資源分配的單位

執行緒是作業系統排程的單位

程序切換需要的資源很最大,效率很低

執行緒切換需要的資源一般,效率一般(當然了在不考慮GIL的情況下)

協程切換任務資源很小,效率高

多程序、多執行緒根據cpu核數不一樣可能是並行的,但是協程是在一個執行緒中 所以是併發

程序:一個執行的程式(程式碼)就是一個程序,沒有執行的程式碼叫程式,程序是系統資源分配的最小單位,程序擁有自己獨立的記憶體空間,所以程序間資料不共享,開銷大。

執行緒: 排程執行的最小單位,也叫執行路徑,不能獨立存在,依賴程序存在一個程序至少有一個執行緒,叫主執行緒,而多個執行緒共享記憶體(資料共享,共享全域性變數),從而極大地提高了程式的執行效率。

協程:是一種使用者態的輕量級執行緒,協程的排程完全由使用者控制。協程擁有自己的暫存器上下文和棧。 協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧,直接操作棧則基本沒有核心切換的開銷,可以不加鎖的訪問全域性變數,所以上下文的切換非常快。

什麼是多執行緒競爭?
執行緒是非獨立的,同一個程序裡執行緒是資料共享的,當各個執行緒訪問資料資源時會出現競爭狀態即:
資料幾乎同步會被多個執行緒佔用,造成資料混亂 ,即所謂的執行緒不安全
那麼怎麼解決多執行緒競爭問題?-- 鎖。
鎖的好處:
確保了某段關鍵程式碼(共享資料資源)只能由一個執行緒從頭到尾完整地執行能解決多執行緒資源競爭下的原子操作問題。

鎖的壞處:
阻止了多執行緒併發執行,包含鎖的某段程式碼實際上只能以單執行緒模式執行,效率就大大地下降了,鎖的致命問題:死鎖

多程序
在Linux系統下,使用os.fork(), 呼叫一次,返回兩次,作業系統自動把當前程序(父程序)複製了一份(子程序),然後分別在父程序和子程序內返回。子程序永遠返回0,父程序返回子程序的ID。經過這樣做,父程序就能fork出很多子程序,並可以記錄下子程序的ID號了,子程序可以通過getppid()來獲取父程序ID。fork()僅在Unix/Linux下使用,windows則不行。 所以,在Python中,存在一個跨平臺的包mutiprocessing,通過引入包中的Process類,就可以建立多程序程式了,可以建立一個程序p=Process(target=func,args=(*,)),然後利用p.start()及p.join()來執行了。以上的join()方法可以等待子程序結束後才往下執行,通常用於程序間同步。 另外,可以用程序池的方式,例如p=Pool(n),然後p.apply_async(func,args),這裡可以使用n種不同的引數傳入,建立不同的程序。用這種方式時,在呼叫join()方法前,要先呼叫close()方法,使得不能再新增新程序。 mutiprocessing包裡提供了Queue、Pipe等多種程序間通訊的方法。可以直接引入Queue類,然後例項化一個物件。則不同的程序可以使用put方法發信息,同時可以使用get方法取資訊。

多執行緒
多個任務可以建立多個程序來完成,同時也可以建立多個執行緒來完成,執行緒是作業系統直接的執行單元。 Python含有threading這個高階模組,要啟動一個執行緒,就是把一個函式傳出並建立Thread例項,然後呼叫start()方法開始執行,例如t=threading.Thread(target=func,name=*),注意這裡的name屬性,它是給執行緒命名的,預設值為Thread-1···。要注意的是,剛才說了,任何一個程序都含有一個執行緒,而這個主執行緒則執行著我們編寫的程式,可以呼叫threading.current_thread().name來檢視它,它的名字就叫MainThread。 在多執行緒程式設計中,有一個最大的問題就在於程序內的資源被各個執行緒所共享,程序內任何變數都可以被任何一個執行緒修改,因此,執行緒之間若去修改同一個變數,則可能導致程式Bug。所以,引入了鎖機制。 當某個執行緒去修改某個變數時,可以在變數所在的方法內加一把鎖,使得其他執行緒不能同時執行該方法,只有釋放了鎖後,其他執行緒才能去獲得鎖並獲得修改權。建立一個鎖是通過lock=threading.Lock()來實現的,可以使用try···finally···語句,在try之前使用lock.acquire()獲得鎖,然後在try語句裡面修改變數,然後在finally語句里加lock.release()來保證鎖一定被釋放,避免成為一個死鎖。
區別於聯絡
多程序的優點是穩定性好,一個子程序崩潰了,不會影響主程序以及其餘程序。但是缺點是建立程序的代價非常大,因為作業系統要給每個程序分配固定的資源,並且,作業系統對程序的總數會有一定的限制,若程序過多,作業系統排程都會存在問題,會造成假死狀態。
多執行緒優點是效率較高一些,但是致命的缺點是任何一個執行緒崩潰都可能造成整個程序的崩潰,因為它們共享了程序的記憶體資源池。 對於任務數來說,無論是多程序或者多執行緒,都不能太多。因為作業系統在切換任務時,會有一系列的保護現場措施,這要花費相當的系統資源,若任務過多,則大部分資源都被用做幹這些了,結果就是所有任務都做不好,所以作業系統會限制程序的數量。 另外,考慮計算密集型及IO密集型應用程式。對於計算密集型,多工勢必造成資源浪費。對於IO密集型,因為IO速度遠低於CPU計算速度,所以使用多工方式可以大大增大程式執行效率。

協程,又稱微執行緒 英文名Coroutine。
協程是python箇中另外一種實現多工的方式,只不過比執行緒更小佔用更小執行單元(理解為需要的資源)。 為啥說它是一個執行單元,因為它自帶CPU上下文。這樣只要在合適的時機, 我們可以把一個協程 切換到另一個協程。 只要這個過程中儲存或恢復 CPU上下文那麼程式還是可以執行的。
在實現多工時, 執行緒切換從系統層面遠不止儲存和恢復 CPU上下文這麼簡單。 作業系統為了程式執行的高效性每個執行緒都有自己快取Cache等等資料,作業系統還會幫你做這些資料的恢復操作。 所以執行緒的切換非常耗效能。但是協程的切換隻是單純的操作CPU的上下文,所以一秒鐘切換個上百萬次系統都抗的住。

協程 -> 微執行緒 在不開闢執行緒的情況下 完成多個任務"交替執行" 網路爬蟲
協程是一個特殊的生成器
yield 返回值 生成器
yield 協程(沒返回值就是協程)
greenlet已經實現了協程,但是這個還的人工切換,是不是覺得太麻煩了,不要捉急,python還有一個比greenlet更強大的並且能夠自動切換任務的模組gevent
其原理是當一個greenlet遇到IO(指的是input output 輸入輸出,比如網路、檔案操作等)操作時,比如訪問網路,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。
由於IO操作非常耗時,經常使程式處於等待狀態,有了gevent為我們自動切換協程,就保證總有greenlet在執行,而不是等待IO