數據與分析工程師的養成---第一次知識分享--Python的多線程與多進

  選這個題目的原因大概是我本來就比較喜歡底層的操作系統甚至更底層的系統架構這方面的東西。因爲之前所學有限,我對多線程和多進程的第一印象是去年暑假實習時候室友做的“MPI並行程序設計”,之前也沒有專門去了解這方面的東西,今天查了一下。

  MPI的全稱是Message-Passing-Interface,中文意思是消息傳遞接口。MPI並不是一種新開發的語言,他是一個定義了可以被C\C++\Fortran程序調用的函數庫。這些函數庫裏面主要涉及的是兩個進程之間通信的函數。MPI在Windows和Linux環境都有相應的庫。

  我們目前的計算機都是基於馮諾依曼結構的,在MIMD(Multiplen Instruction Stream Multiple Data Stream多指令流多數據流,使用多個控制器來異步地控制多個處理器,從而實現空間上的並行性)作爲主要研究對象的系統中,分爲兩種類型:共享內存系統和分佈式內存系統,MPI是屬於分佈式內存系統的方式,還有一種共享內存系統的並行編程方法OpenMP,支持C\C++\Fortran,他主要是用來優化串行的for循環的,他有個缺點是OpenMP編譯器不檢查被parallel for指令並行化的循環所包含的迭代之間的依賴關係,而是有程序員來識別這些依賴關係,一個或更多迭代結果依賴於其他迭代的循環,一般不能被OpenMP正確的並行化。詳情瞭解:https://www.cnblogs.com/hantan2008/p/5361089.html

  那Python的多線程和多進程又有什麼特點呢?

  首先再介紹一下線程和進程,進程是操作系統進行資源分配的最小單元,資源包括CPU,內存,磁盤等IO設備等,而線程是一個控制流CPU調度的基本單位。一個程序至少有一個進程,一個進程至少包含一個線程。舉個簡單的例子來幫助理解:我們電腦上同時運行的瀏覽器和視頻播放器是兩個不同的進程,進程可能包含多個子任務,這些子任務就是線程,比如視頻播放器在播放視頻時要同時顯示圖像、播放聲音、顯示字幕,這就是三個線程。操作系統通過給不同的線程分配時間片(CPU運行時長)來調度線程,當CPU執行完一個線程的時間片後就會快速切換到下一個線程,時間片很短而且切換切速度很快以至於用戶根本察覺不到。早期的計算機是單核單線程的,多個線程根據分配的時間片輪流被CPU執行,如今絕大多數計算機的CPU都是多核的,多個線程在操作系統的調度下能夠被多個CPU併發執行,程序的執行速度和CPU的利用效率大大提升。絕大多數主流的編程語言都能很好地支持多線程,然而python由於GIL鎖無法實現真正的多線程。GIL的全稱是global Intercepto Lock中文是全局解釋器鎖,GIL保證同一時刻只有一個線程執行代碼,每個線程在執行中都要先獲取GIL,GIL只對計算密集型的程序有作用,對IO密集型的程序並沒有影響,因爲遇到IO阻塞會自動釋放GIL鎖,當需要執行計算密集型的程序時,可以選擇:1.換解釋器2.拓展C語言3.換多進程等方案。

  Python線程和進程的比較,一個進程中至少包含一個線程。線程幾乎不佔資源,系統開銷少,切換速度快,而且同一個進程的多線程之間很容易地實現數據共享,而創建進程需要爲他分配單獨的資源,系統開銷大,切換速度慢,而且不同進程之間的數據默認不可共享。根據此特點可以正確的選擇多線程與多進程的編程。

多進程:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431927781401bb47ccf187b24c3b955157bb12c5882d000

多線程:

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143192823818768cd506abbc94eb5916192364506fa5d000

因爲Python的線程雖然是真正的線程,但解釋器執行代碼時,有一個GIL鎖:Global Interpreter Lock,任何Python線程執行前,必須先獲得GIL鎖,然後,每執行100條字節碼,解釋器就自動釋放GIL鎖,讓別的線程有機會執行。這個GIL全局鎖實際上把所有線程的執行代碼都給上了鎖,所以,多線程在Python中只能交替執行,即使100個線程跑在100核CPU上,也只能用到1個核。

GIL是Python解釋器設計的歷史遺留問題,通常我們用的解釋器是官方實現的CPython,要真正利用多核,除非重寫一個不帶GIL的解釋器。

所以,在Python中,可以使用多線程,但不要指望能有效利用多核。如果一定要通過多線程利用多核,那隻能通過C擴展來實現,不過這樣就失去了Python簡單易用的特點。

不過,也不用過於擔心,Python雖然不能利用多線程實現多核任務,但可以通過多進程實現多核任務。多個Python進程有各自獨立的GIL鎖,互不影響。

Unix/Linux操作系統提供了一個fork()系統調用,它非常特殊。普通的函數調用,調用一次,返回一次,但是fork()調用一次,返回兩次,因爲操作系統自動把當前進程(稱爲父進程)複製了一份(稱爲子進程),然後,分別在父進程和子進程內返回。

子進程永遠返回0,而父進程返回子進程的ID。這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調用getppid()就可以拿到父進程的ID。

Python的os模塊封裝了常見的系統調用,其中就包括fork,可以在Python程序中輕鬆創建子進程:

由於Windows沒有fork調用,上面的代碼在Windows上無法運行。由於Mac系統是基於BSD(Unix的一種)內核,所以,在Mac下運行是沒有問題的。

 

線程的狀態:創建 + 就緒 + 運行 + 阻塞(等待) + 退出

創建:一個新的線程被創建,等待該線程被調用執行;
就緒:時間片已用完,此線程被強制暫停,等待下一個屬於他的時間片到來;
運行:此線程正在執行,正在佔用CPU時間片;
阻塞:也叫等待狀態,等待某一事件(如IO或另一個線程)執行完;
退出:一個線程完成任務或者其他終止條件發生,該線程終止進入退出狀態,退出狀態釋放該線程所分配的資源。

早期的進程相當於現在的只有單個線程的進程,現在的多線程也有五種狀態,現在的多線程的生命週期與早期進程的生命週期類似。 

基於進程的並行編程:

multiprocessing 共享內存式編程,系統包含一個或多個處理器,他們都可以訪問到一個公共內存。

mpi4y庫實現了名爲消息傳遞的編程範式,並不會使用共享資源(無共享)並且所有的通信都是通過進程之間交換消息實現的

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章