進程、線程與協程區別總結
- 1.進程是計算器最小資源分配單位
- 2.線程是CPU調度的最小單位
- 3.進程切換需要的資源很最大,效率很低
- 4.線程切換需要的資源一般,效率一般(當然了在不考慮GIL的情況下)
- 5.協程切換任務資源很小,效率高(協程本身並不存在,是程序員通過控制IO操作完成)
- 6.多進程、多線程根據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
