㈠ 一篇文章帶你深度解析python線程和進程
使用Python中的線程模塊,能夠同時運行程序的不同部分,並簡化設計。如果你已經入門Python,並且想用線程來提升程序運行速度的話,希望這篇教程會對你有所幫助。
線程與進程
什麼是進程
進程是系統進行資源分配和調度的一個獨立單位 進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。每個進程都有自己的獨立內存空間,不同進程通過進程間通信來通信。由於進程比較重量,占據獨立的內存,所以上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,但相對比較穩定安全。
什麼是線程
CPU調度和分派的基本單位 線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。線程間通信主要通過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。
進程與線程的關系圖
線程與進程的區別:
進程
現實生活中,有很多的場景中的事情是同時進行的,比如開車的時候 手和腳共同來駕駛 汽車 ,比如唱歌跳舞也是同時進行的,再比如邊吃飯邊打電話;試想如果我們吃飯的時候有一個領導來電,我們肯定是立刻就接聽了。但是如果你吃完飯再接聽或者回電話,很可能會被開除。
注意:
多任務的概念
什麼叫 多任務 呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器上網,一邊在聽MP3,一邊在用Word趕作業,這就是多任務,至少同時有3個任務正在運行。還有很多任務悄悄地在後台同時運行著,只是桌面上沒有顯示而已。
現在,多核CPU已經非常普及了,但是,即使過去的單核CPU,也可以執行多任務。由於CPU執行代碼都是順序執行的,那麼,單核CPU是怎麼執行多任務的呢?
答案就是操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒,這樣反復執行下去。表面上看,每個任務都是交替執行的,但是,由於CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
真正的並行執行多任務只能在多核CPU上實現,但是,由於任務數量遠遠多於CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。 其實就是CPU執行速度太快啦!以至於我們感受不到在輪流調度。
並行與並發
並行(Parallelism)
並行:指兩個或兩個以上事件(或線程)在同一時刻發生,是真正意義上的不同事件或線程在同一時刻,在不同CPU資源呢上(多核),同時執行。
特點
並發(Concurrency)
指一個物理CPU(也可以多個物理CPU) 在若幹道程序(或線程)之間多路復用,並發性是對有限物理資源強制行使多用戶共享以提高效率。
特點
multiprocess.Process模塊
process模塊是一個創建進程的模塊,藉助這個模塊,就可以完成進程的創建。
語法:Process([group [, target [, name [, args [, kwargs]]]]])
由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動)。
注意:1. 必須使用關鍵字方式來指定參數;2. args指定的為傳給target函數的位置參數,是一個元祖形式,必須有逗號。
參數介紹:
group:參數未使用,默認值為None。
target:表示調用對象,即子進程要執行的任務。
args:表示調用的位置參數元祖。
kwargs:表示調用對象的字典。如kwargs = {'name':Jack, 'age':18}。
name:子進程名稱。
代碼:
除了上面這些開啟進程的方法之外,還有一種以繼承Process的方式開啟進程的方式:
通過上面的研究,我們千方百計實現了程序的非同步,讓多個任務可以同時在幾個進程中並發處理,他們之間的運行沒有順序,一旦開啟也不受我們控制。盡管並發編程讓我們能更加充分的利用IO資源,但是也給我們帶來了新的問題。
當多個進程使用同一份數據資源的時候,就會引發數據安全或順序混亂問題,我們可以考慮加鎖,我們以模擬搶票為例,來看看數據安全的重要性。
加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串列的修改。加鎖犧牲了速度,但是卻保證了數據的安全。
因此我們最好找尋一種解決方案能夠兼顧:1、效率高(多個進程共享一塊內存的數據)2、幫我們處理好鎖問題。
mutiprocessing模塊為我們提供的基於消息的IPC通信機制:隊列和管道。隊列和管道都是將數據存放於內存中 隊列又是基於(管道+鎖)實現的,可以讓我們從復雜的鎖問題中解脫出來, 我們應該盡量避免使用共享數據,盡可能使用消息傳遞和隊列,避免處理復雜的同步和鎖問題,而且在進程數目增多時,往往可以獲得更好的可獲展性( 後續擴展該內容 )。
線程
Python的threading模塊
Python 供了幾個用於多線程編程的模塊,包括 thread, threading 和 Queue 等。thread 和 threading 模塊允許程序員創建和管理線程。thread 模塊 供了基本的線程和鎖的支持,而 threading 供了更高級別,功能更強的線程管理的功能。Queue 模塊允許用戶創建一個可以用於多個線程之間 共享數據的隊列數據結構。
python創建和執行線程
創建線程代碼
1. 創建方法一:
2. 創建方法二:
進程和線程都是實現多任務的一種方式,例如:在同一台計算機上能同時運行多個QQ(進程),一個QQ可以打開多個聊天窗口(線程)。資源共享:進程不能共享資源,而線程共享所在進程的地址空間和其他資源,同時,線程有自己的棧和棧指針。所以在一個進程內的所有線程共享全局變數,但多線程對全局變數的更改會導致變數值得混亂。
代碼演示:
得到的結果是:
首先需要明確的一點是GIL並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。就好比C++是一套語言(語法)標准,但是可以用不同的編譯器來編譯成可執行代碼。同樣一段代碼可以通過CPython,PyPy,Psyco等不同的Python執行環境來執行(其中的JPython就沒有GIL)。
那麼CPython實現中的GIL又是什麼呢?GIL全稱Global Interpreter Lock為了避免誤導,我們還是來看一下官方給出的解釋:
主要意思為:
因此,解釋器實際上被一個全局解釋器鎖保護著,它確保任何時候都只有一個Python線程執行。在多線程環境中,Python 虛擬機按以下方式執行:
由於GIL的存在,Python的多線程不能稱之為嚴格的多線程。因為 多線程下每個線程在執行的過程中都需要先獲取GIL,保證同一時刻只有一個線程在運行。
由於GIL的存在,即使是多線程,事實上同一時刻只能保證一個線程在運行, 既然這樣多線程的運行效率不就和單線程一樣了嗎,那為什麼還要使用多線程呢?
由於以前的電腦基本都是單核CPU,多線程和單線程幾乎看不出差別,可是由於計算機的迅速發展,現在的電腦幾乎都是多核CPU了,最少也是兩個核心數的,這時差別就出來了:通過之前的案例我們已經知道,即使在多核CPU中,多線程同一時刻也只有一個線程在運行,這樣不僅不能利用多核CPU的優勢,反而由於每個線程在多個CPU上是交替執行的,導致在不同CPU上切換時造成資源的浪費,反而會更慢。即原因是一個進程只存在一把gil鎖,當在執行多個線程時,內部會爭搶gil鎖,這會造成當某一個線程沒有搶到鎖的時候會讓cpu等待,進而不能合理利用多核cpu資源。
但是在使用多線程抓取網頁內容時,遇到IO阻塞時,正在執行的線程會暫時釋放GIL鎖,這時其它線程會利用這個空隙時間,執行自己的代碼,因此多線程抓取比單線程抓取性能要好,所以我們還是要使用多線程的。
GIL對多線程Python程序的影響
程序的性能受到計算密集型(CPU)的程序限制和I/O密集型的程序限制影響,那什麼是計算密集型和I/O密集型程序呢?
計算密集型:要進行大量的數值計算,例如進行上億的數字計算、計算圓周率、對視頻進行高清解碼等等。這種計算密集型任務雖然也可以用多任務完成,但是花費的主要時間在任務切換的時間,此時CPU執行任務的效率比較低。
IO密集型:涉及到網路請求(time.sleep())、磁碟IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和內存的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。
當然為了避免GIL對我們程序產生影響,我們也可以使用,線程鎖。
Lock&RLock
常用的資源共享鎖機制:有Lock、RLock、Semphore、Condition等,簡單給大家分享下Lock和RLock。
Lock
特點就是執行速度慢,但是保證了數據的安全性
RLock
使用鎖代碼操作不當就會產生死鎖的情況。
什麼是死鎖
死鎖:當線程A持有獨占鎖a,並嘗試去獲取獨占鎖b的同時,線程B持有獨占鎖b,並嘗試獲取獨占鎖a的情況下,就會發生AB兩個線程由於互相持有對方需要的鎖,而發生的阻塞現象,我們稱為死鎖。即死鎖是指多個進程因競爭資源而造成的一種僵局,若無外力作用,這些進程都將無法向前推進。
所以,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確定資源的合理分配演算法,避免進程永久占據系統資源。
死鎖代碼
python線程間通信
如果各個線程之間各干各的,確實不需要通信,這樣的代碼也十分的簡單。但這一般是不可能的,至少線程要和主線程進行通信,不然計算結果等內容無法取回。而實際情況中要復雜的多,多個線程間需要交換數據,才能得到正確的執行結果。
python中Queue是消息隊列,提供線程間通信機制,python3中重名為為queue,queue模塊塊下提供了幾個阻塞隊列,這些隊列主要用於實現線程通信。
在 queue 模塊下主要提供了三個類,分別代表三種隊列,它們的主要區別就在於進隊列、出隊列的不同。
簡單代碼演示
此時代碼會阻塞,因為queue中內容已滿,此時可以在第四個queue.put('蘋果')後面添加timeout,則成為 queue.put('蘋果',timeout=1)如果等待1秒鍾仍然是滿的就會拋出異常,可以捕獲異常。
同理如果隊列是空的,無法獲取到內容默認也會阻塞,如果不阻塞可以使用queue.get_nowait()。
在掌握了 Queue 阻塞隊列的特性之後,在下面程序中就可以利用 Queue 來實現線程通信了。
下面演示一個生產者和一個消費者,當然都可以多個
使用queue模塊,可在線程間進行通信,並保證了線程安全。
協程
協程,又稱微線程,纖程。英文名Coroutine。
協程是python個中另外一種實現多任務的方式,只不過比線程更小佔用更小執行單元(理解為需要的資源)。為啥說它是一個執行單元,因為它自帶CPU上下文。這樣只要在合適的時機, 我們可以把一個協程 切換到另一個協程。只要這個過程中保存或恢復 CPU上下文那麼程序還是可以運行的。
通俗的理解:在一個線程中的某個函數,可以在任何地方保存當前函數的一些臨時變數等信息,然後切換到另外一個函數中執行,注意不是通過調用函數的方式做到的,並且切換的次數以及什麼時候再切換到原來的函數都由開發者自己確定。
在實現多任務時,線程切換從系統層面遠不止保存和恢復 CPU上下文這么簡單。操作系統為了程序運行的高效性每個線程都有自己緩存Cache等等數據,操作系統還會幫你做這些數據的恢復操作。所以線程的切換非常耗性能。但是協程的切換只是單純的操作CPU的上下文,所以一秒鍾切換個上百萬次系統都抗的住。
greenlet與gevent
為了更好使用協程來完成多任務,除了使用原生的yield完成模擬協程的工作,其實python還有的greenlet模塊和gevent模塊,使實現協程變的更加簡單高效。
greenlet雖說實現了協程,但需要我們手工切換,太麻煩了,gevent是比greenlet更強大的並且能夠自動切換任務的模塊。
其原理是當一個greenlet遇到IO(指的是input output 輸入輸出,比如網路、文件操作等)操作時,比如訪問網路,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。
模擬耗時操作:
如果有耗時操作也可以換成,gevent中自己實現的模塊,這時候就需要打補丁了。
使用協程完成一個簡單的二手房信息的爬蟲代碼吧!
以下文章來源於Python專欄 ,作者宋宋
文章鏈接:https://mp.weixin.qq.com/s/2r3_ipU3HjdA5VnqSHjUnQ
㈡ 適合初學者的頂級Python書單
Python 新手?或者您已經是一位經驗豐富的開發人員,希望提升您的 Python知識?可以看一下教務老師推薦的書單,適合所有級別的Python開發程序員。
如果您是初學者,請參考這兩本書。
Python編程快速上手 讓繁瑣工作自動化 第2版
程序員不需要知道太多數學知識
我聽到的關於學習編程的最常見的顧慮,就是人們認為這需要很多數學知識。其實,大多數編程需要的數學知識不外乎基本算術運算。實際上,善於編程與善於解決數獨問題沒有太大差別。
要解決數獨問題,數字1~9必須填入9×9棋盤的每一行、每一列,以及每個3×3的內部方塊。系統提供了一些數字來幫助你開始,然後你可以根據這些數字進行推算,從而找到答案。例如,在圖 0-1的數獨問題中,既然5出現在了第1行和第2行,它就不能在這些行中再次出現。因此,在右上角的3×3方塊中,它必定在第3行;由於整個網格的最後一列已有了5,所以在右上角的3×3方塊中,5就不能在6的右邊。每次解決一行、一列或一個方塊,將為剩下的部分提供更多的數字線索。隨著你填入一組數字1~9,然後再填寫另一組數字,整個網格很快就會被填滿。
圖0-1 一個新的數獨問題(左邊)及其答案(右邊)。盡管使用了數字,但數獨並不需要太多數學知識
數獨雖然使用了數字,但兄扮顫並不意味著必須精通數學才能求出答案。編程也是這樣。就像解決數獨問題一樣,編程需要將一個問題分解為單個的、詳細的步驟。類似地,在「調試」程序(即尋找和修復錯誤)時,你會耐心地觀察程序在做什麼,找出出現錯誤的原因。像所有技能一樣,編寫的程序越多,你掌握得就越好。
就本書來說,它不會讓你變成一個職業軟體開發者,就像學習幾節吉他課程不會讓你變成一名搖滾明星一樣。但如果你是辦公室職員、管理者羨敗、學術研究者,或其他任何使用計算機來工作缺鏈或娛樂的人,通過本書,你將學到編程的基本知識,這樣就能將下面這些簡單的任務自動化。
㈢ java學習主要是學習什麼呢
你好,學習java只要掌握好方式和方法,其實學起來並不是非常難。比如你可以自學也可以選擇機構學。
java是目前主流的開發語言,程序員不論是大數據、雲計算、web前端、後端開發等都需要從java學起,如果你想計入IT高薪行列,建議學java!
java學的內容主要有:
①JAVA編程基礎(基礎語法、面向對象、和諧特性等)
②WEB應用開發(靜態網頁製作、Oracle資料庫、Java Web開發技術、Linux技術、網站性能與安全、軟體工程開發流程、Java Web和諧等)
③企業級框架開發(數據結構與演算法、SSH框架、JavaEE和諧等)
④項目實訓
互聯網行業目前還是最熱門的行業之一,學習IT技能之後足夠優秀是有機會進入騰訊、阿里、網易等互聯網大廠高薪就業的,發展前景非常好,普通人也可以學習。
想要系統學習,你可以考察對比一下開設有相關專業的熱門學校,好的學校擁有根據當下企業需求自主研發課程的能力,能夠在校期間取得大專或本科學歷,中博軟體學院、南京課工場、南京北大青鳥等開設相關專業的學校都是不錯的,建議實地考察對比一下。
祝你學有所成,望採納。
㈣ 計算機課操作系統的作用是
用一個簡單的案例分析我們我可以推斷,操作系統的意義不是去教會學生怎樣去編寫他們自己的操作系統。第一,這里有些學生對操作系統有興趣並能編寫一個操作系統。他們不需要看課堂上的資料就能夠很好地編寫自己的操作系統。第二,我們也有一些學生沒能力實現一個新的的操作系統或者對實現新的操作系統沒興趣的。他們同樣不需要課堂上的資料。
那麼,意義何在呢?
並發
編寫並發代碼並不容易,特別是使用線程共享內存和線程鎖。然而,現在很多學習計算機科學的學生都會在他們以後的職業生涯的某些時候使用到(並發)。在OS課程以外的課程里學習並發問題已經成為了一種增長的趨勢,但即便如此,操作系統是學生首次了解線程,競爭,死鎖等等重要概念的傳統課程。教材很難(實際上很簡單的,但運用起來很難),在畢業前多看幾次是很有幫助的。一個可靠的並發編程介紹對學習操作系統課程是有很大好處的。
資源管理
硬體層次上的資源通常是專用的。操作系統提供了這些資源的種類,它們可以是虛擬的(每個用戶都有種錯覺,自己擁有資源的一份備份)或者是仲裁的(一次只能有一個用戶佔有資源,但由操作系統來安排訪問順序)。允許多用戶訪問專用物理資源是一個很基本的策略並被運用到很多用戶級別的程序中。通過詳細地學習這些內容,學生學會了能夠在許多其他場合重用的模式。
性能分析和沖突解決
正如「為什麼#*$是我的機器分頁?」。當資源被分享時,沖突通常也會隨之而來。沖突問題可以使用多種方式來解決,比如使用隊列,合理共享,或者使用優先順序。在某些情況下,比如CPU調度,沒有單一技術解決方案並且最後的解決方案是一些古怪的混合技術。有時,最令人感興趣的是找出導致問題出現的主要原因是哪一類沖突。我花費了夏天的一大部分時間去找出所有Windows NT導致MP3跳過的原因。操作系統課是學習這些理論的完美課程,它的適用性比計算機科學更廣泛。
隱藏復雜性的介面
一個具有良好設計的介面是一個美妙的東西。它更美妙的地方體現在把一個討厭的低層次介面(數據機或者NE2000卡)轉換為一個實用高效的高層次抽象介面(套接字流)。學生應該已經在教材里關於抽象數據類型那部分接觸過這些想法了,給定的例子一般都是比較普通的,並且抽象化和隱藏復雜性的作用在那個層次里不夠明顯。我認為把像套接字(socket),文件系統和地址空間這些集合合到一個單一便利的包里可能是計算機科學10大貢獻之一了。這是司空見慣的事,以至於很容易讓人忽視它的迷人之處。
沒什麼神奇的
從用戶模式(user mode)看,很容易發現OS是一個神奇的東西–它提供了流暢的多任務處理,高效擦儲存管理等。–不好的–它會出現藍屏,系統顛簸,安全問題和調度異常。對於一般用戶來說,這個模式是好的。但在另一方面,如果你想去證明你是一個計算機科學家,你需要知道這些問題的幕後細節。你將會從那裡發現什麼?很多時候,這看起來都是一些令人憂愁的集合,比如單調的鏈表,狡猾的啟發式資源和維護不當的設備驅動程序。好的OS課程應該教會學生這些:
在內核的代碼很優秀,你只需要知道到哪裡找到它們。當你第一次看到它們時,你不一定會理解它們。但你理解了它們,你就會學得更多。
通常,內核代碼都是很普通的代碼。任何人都可以編寫它,對比於用戶模式代碼(user-mode code),內核代碼(kernel code)僅需要多一點對細節的關心和注意,因為內核代碼中的bug造成的結果更嚴重。
處理大型軟體
這是毫無疑問的,陷入別人的幾百萬行代碼庫中去是一個噩夢。錯誤零散的文檔,殘舊和廣泛的介面,糟糕的交互,和費解的錯誤信息。不過,歡迎回到現實世界,我們不能因為這些糟糕的問題就經常重新開始。作為一個學生,如果你能夠開始制定一個系統的方法去學習你需要用代碼修復的大軟體的相關部分,那麼你以後的生活就會輕松很多。你可以討厭Linux內核但它比你以後的職業生涯會遇到的軟體好多了。
計算機系統設計
設計任何的工程系統,包括軟體系統,都是一個權衡的過程。是要側重於可靠性?性能?消耗還是維護性?因為操作系統是很龐大的,性能至關重要的程序,它一般都要維護幾十年,所以它們是學生學習這類權衡的很好的地方。擁有一雙發現合適設計點的銳利眼睛的學生在工業上是很需要的。這些人更像一個藝術家而不是一個科學家,你需要看大量的代碼,理解這些問題,和學會自己獨立考慮這些問題。
總結
我已經嘗試去說清楚,一門OS課程不僅僅是關於操作系統和給UNIX/Windows/MacOS的使用者提供知識。優秀的OS課程教會你對廣泛使用的操作系統的思考技巧和方式,即使你從沒接觸過一行的內核代碼。實際上,在我的大學里獲取學位的CS學生不要求一定要上OS課程,但我覺得,所有真正的計算機科學家要麼是已經學這么課,要麼已經用其他方式學會了這方面的技巧和直接
㈤ Pthread 並發編程(三)——深入理解線程取消機制
本文深入剖析了Pthread並發編程中的線程取消機制,這是一種用於終止線程執行的功能,僅在共享內存的多線程環境中有效。下面通過實例來呈現其工作原理。
在示常式序中,主線程調用pthread_cancel取消運行中的線程,如函數task,結果顯示出線程在列印"step1"後被中斷,證明了線程被成功取消。
深入分析指出,當線程被正常取消後,pthread_join用來獲取線程退出狀態,若返回值為PTHREAD_CANCELED,證明了取消機制的正確性。我們還研究了pthread_cancel的函數簽名和可能的返回值,例如ESRCH的錯誤情況。
線程取消機制的執行涉及到線程的狀態和取消類型,分為兩種狀態和兩種類型。當線程設置為不接受取消請求時,取消無效。通過pthread_setcancelstate函數可以控制線程的取消狀態。
clean-up handlers在線程被取消時執行,如通過pthread_cleanup_push添加清理函數。函數func中的clean-up handler演示了這一過程。線程退出時,pthread_exit和pthread_cancel都會按照特定順序執行clean-up handlers。
此外,文章還涉及了線程私有數據的概念,通過pthread_key_create和pthread_setspecific來管理線程獨有的數據,這些數據在線程結束時會被正確析構,釋放內存。
總結來說,線程取消機制的核心流程包括發送取消請求、響應取消、執行清理操作和析構線程私有數據。理解這些細節有助於在實際編程中更有效地控制和管理線程行為。後續文章將探討更多並發編程主題,如線程調度和同步。
想要獲取更多深入內容,可訪問項目github.com/Chang-LeHung...,或關注公眾號「一無是處的研究僧」,探索更多計算機技術知識。