作業系統-多進程和多執行緒-python
- 2019 年 11 月 24 日
- 筆記
在學習廖雪峰老師的python教程,學習了多進程和多執行緒,記錄下核心的思路和方法。
多任務:簡單地說,就是作業系統可以同時運行多個任務
單核CPU是怎麼執行多任務的呢?k
答案就是作業系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒……這樣反覆執行下去。表面上看,每個任務都是交替執行的,但是,由於CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
對於作業系統來說,一個任務就是一個進程(Process)有些進程還不止同時干一件事,比如Word,它可以同時進行打字、拼寫檢查、列印等事情。在一個進程內部,要同時干多件事,就需要同時運行多個「子任務」,我們把進程內的這些「子任務」稱為執行緒(Thread)。
由於每個進程至少要干一件事,所以,一個進程至少有一個執行緒。當然,像Word這種複雜的進程可以有多個執行緒,多個執行緒可以同時執行,多執行緒的執行方式和多進程是一樣的,也是由作業系統在多個執行緒之間快速切換,讓每個執行緒都短暫地交替運行,看起來就像同時執行一樣。當然,真正地同時執行多執行緒需要多核CPU才可能實現。
如果我們要同時執行多個任務怎麼辦?
有兩種解決方案:
一種是啟動多個進程,每個進程雖然只有一個執行緒,但多個進程可以一塊執行多個任務。
還有一種方法是啟動一個進程,在一個進程內啟動多個執行緒,這樣,多個執行緒也可以一塊執行多個任務。
當然還有第三種方法,就是啟動多個進程,每個進程再啟動多個執行緒,這樣同時執行的任務就更多了,當然這種
執行緒是最小的執行單元,而進程由至少一個執行緒組成。如何調度進程和執行緒,完全由作業系統決定,程式自己不能決定什麼時候執行,執行多長時間。
多進程和多執行緒的程式涉及到同步、數據共享的問題,編寫起來更複雜。
模型更複雜,實際很少採用。
總結一下就是,多任務的實現有3種方式:
- 多進程模式;
- 多執行緒模式;
- 多進程+多執行緒模式。
同時執行多個任務通常各個任務之間並不是沒有關聯的,而是需要相互通訊和協調,有時,任務1必須暫停等待任務2完成後才能繼續執行,有時,任務3和任務4又不能同時執行,所以,多進程和多執行緒的程式的複雜度要遠遠高於我們前面寫的單進程單執行緒的程式。
因為複雜度高,調試困難,所以,不是迫不得已,我們也不想編寫多任務。但是,有很多時候,沒有多任務還真不行。想想在電腦上看電影,就必須由一個執行緒播放影片,另一個執行緒播放音頻,否則,單執行緒實現的話就只能先把影片播放完再播放音頻,或者先把音頻播放完再播放影片,這顯然是不行的。
Python既支援多進程,又支援多執行緒,
執行緒是最小的執行單元,而進程由至少一個執行緒組成。如何調度進程和執行緒,完全由作業系統決定,程式自己不能決定什麼時候執行,執行多長時間。
多進程和多執行緒的程式涉及到同步、數據共享的問題,編寫起來更複雜。
Unix/Linux作業系統提供了一個fork()
系統調用,它非常特殊。普通的函數調用,調用一次,返回一次,但是fork()
調用一次,返回兩次,因為作業系統自動把當前進程(稱為父進程)複製了一份(稱為子進程),然後,分別在父進程和子進程內返回。
子進程永遠返回0
,而父進程返回子進程的ID。這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調用getppid()
就可以拿到父進程的ID。
Python的os
模組封裝了常見的系統調用,其中就包括fork
,可以在Python程式中輕鬆創建子進程:
由於Python是跨平台的,自然也應該提供一個跨平台的多進程支援。multiprocessing
模組就是跨平台版本的多進程模組。
multiprocessing
模組提供了一個Process
類來代表一個進程對象,下面的例子演示了啟動一個子進程並等待其結束:
創建子進程時,只需要傳入一個執行函數和函數的參數,創建一個Process
實例,用start()
方法啟動,這樣創建進程比fork()
還要簡單。
join()
方法可以等待子進程結束後再繼續往下運行,通常用於進程間的同步。
Pool
如果要啟動大量的子進程,可以用進程池的方式批量創建子進程:
對Pool
對象調用join()
方法會等待所有子進程執行完畢,調用join()
之前必須先調用close()
,調用close()
之後就不能繼續添加新的Process
了。
請注意輸出的結果,task 0
,1
,2
,3
是立刻執行的,而task 4
要等待前面某個task完成後才執行,這是因為Pool
的默認大小在我的電腦上是4,因此,最多同時執行4個進程。這是Pool
有意設計的限制,並不是作業系統的限制。如果改成:
Python
p = Pool(5)
1 |
p = Pool(5) |
---|
就可以同時跑5個進程。
子進程
很多時候,子進程並不是自身,而是一個外部進程。我們創建了子進程後,還需要控制子進程的輸入和輸出。
subprocess
模組可以讓我們非常方便地啟動一個子進程,然後控制其輸入和輸出。
下面的例子演示了如何在Python程式碼中運行命令nslookup www.python.org
,這和命令行直接運行的效果是一樣的:
進程間通訊
Process
之間肯定是需要通訊的,作業系統提供了很多機制來實現進程間的通訊。Python的multiprocessing
模組包裝了底層的機制,提供了Queue
、Pipes
等多種方式來交換數據。
我們以Queue
為例,在父進程中創建兩個子進程,一個往Queue
里寫數據,一個從Queue
里讀數據:
在Unix/Linux下,multiprocessing
模組封裝了fork()
調用,使我們不需要關注fork()
的細節。由於Windows沒有fork
調用,因此,multiprocessing
需要「模擬」出fork
的效果,父進程所有Python對象都必須通過pickle序列化再傳到子進程去,所有,如果multiprocessing
在Windows下調用失敗了,要先考慮是不是pickle失敗了。
小結
在Unix/Linux下,可以使用fork()
調用實現多進程。
要實現跨平台的多進程,可以使用multiprocessing
模組。
進程間通訊是通過Queue
、Pipes
等實現的。
原創文章,轉載請註明: 轉載自URl-team
本文鏈接地址: 作業系統-多進程和多執行緒-python