9 異常處理 操作系統 進程線程 隊列+生產消費者模型 進程同步 回調函數
異常處理
異常就是程序運行時發生錯誤的信號,在python中,錯誤觸發的異常如下
異常的種類:
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x IOError 輸入/輸出異常;基本上是無法打開文件 ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤 IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊 IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5] KeyError 試圖訪問字典裏不存在的鍵 KeyboardInterrupt Ctrl+C被按下 NameError 使用一個還未被賦予對象的變量 SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了) TypeError 傳入對象類型與要求的不符合 UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量, 導致你以為正在訪問它 ValueError 傳入一個調用者不期望的值,即使值的類型是正確的常用異常
異常處理:
python為每一種異常定制了一個類型,然後提供了一種特定的語法結構用來進行異常處理。
1 try: 2 被檢測的代碼塊 3 except 異常類型: 4 try中一旦檢測到異常,就執行這個位置的邏輯基本語法
異常類只能用來處理指定的異常情況,如果非指定異常則無法處理。
1 # 未捕獲到異常,程序直接報錯 2 3 s1 = ‘hello‘ 4 try: 5 int(s1) 6 except IndexError as e: 7 print eView Code
多分支
1 s1 = ‘hello‘ 2 try: 3 int(s1) 4 except IndexError as e: 5 print(e) 6 except KeyError as e: 7 print(e) 8 except ValueError as e: 9 print(e)View Code
萬能異常
在python的異常中,有一個萬能異常:Exception,他可以捕獲任意異常,即:
1 s1 = ‘hello‘ 2 try: 3 int(s1) 4 except Exception as e:View Code5 print(e)
1、如果你統一用Exception,沒錯,是可以捕捉所有異常,但意味著你在處理所有異常時都使用同一個邏輯去處理(這裏說的邏輯即當前expect下面跟的代碼塊)
2.如果你想要的效果是,對於不同的異常我們需要定制不同的處理邏輯,那就需要用到多分支了。
s1 = ‘hello‘ try: int(s1) except IndexError as e: print(e) except KeyError as e: print(e) except ValueError as e: print(e)多分支
結論:
只有在有些異常無法預知的情況下,才應該加上try...except,其他的邏輯錯誤應該盡量修正。
操作系統
程序員無法把所有的硬件操作細節都了解到,管理這些硬件並且加以優化使用是非常繁瑣的工作,這個繁瑣的工作就是操作系統來幹的,有了他,程序員就從這些繁瑣的工作中解脫了出來,只需要考慮自己的應用軟件的編寫就可以了,應用軟件直接使用操作系統提供的功能來間接使用硬件。
精簡的說的話,操作系統就是一個協調、管理和控制計算機硬件資源和軟件資源的控制程序。
操作系統的兩大作用:
作用一:為應用程序提供如何使用硬件資源的抽象
作用二:管理硬件資源
二:多路復用
現代計算機或者網絡都是多用戶的,多個用戶不僅共享硬件,而且共享文件,數據庫等信息,共享意味著沖突和無序。
操作系統主要使用來
1.記錄哪個程序使用什麽資源
2.對資源請求進行分配
3.為不同的程序和用戶調解互相沖突的資源請求。
我們可將上述操作系統的功能總結為:處理來自多個程序發起的多個(多個即多路)共享(共享即復用)資源的請求,簡稱多路復用。
1.時間上的復用
當一個資源在時間上復用時,不同的程序或用戶輪流使用它,第一個程序獲取該資源使用結束後,在輪到第二個。。。第三個。。。
例如:只有一個cpu,多個程序需要在該cpu上運行,操作系統先把cpu分給第一個程序,在這個程序運行的足夠長的時間(時間長短由操作系統的算法說了算)或者遇到了I/O阻塞,操作系統則把cpu分配給下一個程序,以此類推,直到第一個程序重新被分配到了cpu然後再次運行,由於cpu的切換速度很快,給用戶的感覺就是這些程序是同時運行的,或者說是並發的,或者說是偽並行的。至於資源如何實現時間復用,或者說誰應該是下一個要運行的程序,以及一個任務需要運行多長時間,這些都是操作系統的工作。
2.空間上的復用
每個客戶都獲取了一個大的資源中的一小部分資源,從而減少了排隊等待資源的時間。
例如:多個運行的程序同時進入內存,硬件層面提供保護機制來確保各自的內存是分割開的,且由操作系統控制,這比一個程序獨占內存一個一個排隊進入內存效率要高的多。
有關空間復用的其他資源還有磁盤,在許多系統中,一個磁盤同時為許多用戶保存文件。分配磁盤空間並且記錄誰正在使用哪個磁盤塊是操作系統資源管理的典型任務。
這兩種方式合起來便是多道技術
進程線程
對於操作系統來說,一個任務就是一個進程(Process),比如打開一個瀏覽器就是啟動一個瀏覽器進程,打開一個記事本就啟動了一個記事本進程,打開兩個記事本就啟動了兩個記事本進程,打開一個Word就啟動了一個Word進程。
有些進程還不止同時幹一件事,比如Word,它可以同時進行打字、拼寫檢查、打印等事情。在一個進程內部,要同時幹多件事,就需要同時運行多個“子任務”,我們把進程內的這些“子任務”稱為線程(Thread)。
由於每個進程至少要幹一件事,所以,一個進程至少有一個線程。當然,像Word這種復雜的進程可以有多個線程,多個線程可以同時執行,多線程的執行方式和多進程是一樣的,也是由操作系統在多個線程之間快速切換,讓每個線程都短暫地交替運行,看起來就像同時執行一樣。當然,真正地同時執行多線程需要多核CPU才可能實現。
我們前面編寫的所有的Python程序,都是執行單任務的進程,也就是只有一個線程。如果我們要同時執行多個任務怎麽辦?
有兩種解決方案:
一種是啟動多個進程,每個進程雖然只有一個線程,但多個進程可以一塊執行多個任務。
還有一種方法是啟動一個進程,在一個進程內啟動多個線程,這樣,多個線程也可以一塊執行多個任務。
當然還有第三種方法,就是啟動多個進程,每個進程再啟動多個線程,這樣同時執行的任務就更多了,當然這種模型更復雜,實際很少采用。
總結一下就是,多任務的實現有3種方式:
? 多進程模式;
? 多線程模式;
? 多進程+多線程模式。
同時執行多個任務通常各個任務之間並不是沒有關聯的,而是需要相互通信和協調,有時,任務1必須暫停等待任務2完成後才能繼續執行,有時,任務3和任務4又不能同時執行,所以,多進程和多線程的程序的復雜度要遠遠高於前面寫的單進程單線程的程序。
有很多時候,沒有多任務還真不行。想想在電腦上看電影,就必須由一個線程播放視頻,另一個線程播放音頻,否則,單線程實現的話就只能先把視頻播放完再播放音頻,或者先把音頻播放完再播放視頻,這顯然是不行的。
Python既支持多進程,又支持多線程。
總結:
線程是最小的執行單元,而進程由至少一個線程組成。如何調度進程和線程,完全由操作系統決定,程序自己不能決定什麽時候執行,執行多長時間。
多進程和多線程的程序涉及到同步、數據共享的問題,編寫起來更復雜。
並發與並行
無論是並行還是並發,在用戶看來都是‘同時‘運行的,不管是進程還是線程,都只是一個任務而已,真是幹活的是cpu,cpu來做這些任務,而一個cpu同一時刻只能執行一個任務
並行:同時運行,只有具備多個cpu才能實現並行
並發:是偽並行,即看起來是同時運行。單個cpu+多道技術就可以實現並發,(並行也屬於並發)
線程
在傳統操作系統中,每個進程有一個地址空間,而且默認就有一個控制線程
多線程(即多個控制線程)的概念是,在一個進程中存在多個控制線程,多個控制線程共享該進程的地址空間
進程只是用來把資源集中到一起(進程只是一個資源單位,或者說資源集合),而線程才是cpu上的執行單位,
例如,北京地鐵與上海地鐵是不同的進程,而北京地鐵裏的13號線是一個線程,北京地鐵所有的線路共享北京地鐵所有的資源,比如所有的乘客可以被所有線路拉。
為何要用多線程
多線程指的是,在一個進程中開啟多個線程,簡單的講:如果多個任務共用一塊地址空間,那麽必須在一個進程內開啟多個線程。詳細的講分為4點:
1. 多線程共享一個進程的地址空間
2. 線程比進程更輕量級,線程比進程更容易創建可撤銷,在許多操作系統中,創建一個線程比創建一個進程要快10-100倍,在有大量線程需要動態和快速修改時,這一特性很有用
3. 若多個線程都是cpu密集型的,那麽並不能獲得性能上的增強,但是如果存在大量的計算和大量的I/O處理,擁有多個線程允許這些活動彼此重疊運行,從而會加快程序執行的速度。
4. 在多cpu系統中,為了最大限度的利用多核,可以開啟多個線程(比開進程開銷要小的多)
多線程應用舉例:
開啟一個字處理軟件進程,該進程肯定需要辦不止一件事情,比如監聽鍵盤輸入,處理文字,定時自動將文字保存到硬盤,這三個任務操作的都是同一塊數據,因而不能用多進程。只能在一個進程裏並發地開啟三個線程,如果是單線程,那就只能是,鍵盤輸入時,不能處理文字和自動保存,自動保存時又不能輸入和處理文字。
python並發編程之多進程
multiprocessing模塊介紹
python中的多線程無法利用多核優勢,如果想要充分地使用多核CPU的資源(os.cpu_count()查看),在python中大部分情況需要使用多進程。Python提供了非常好用的多進程包multiprocessing。
multiprocessing模塊用來開啟子進程,並在子進程中執行我們定制的任務(比如函數),該模塊與多線程模塊threading的編程接口類似。
multiprocessing模塊的功能眾多:支持子進程、通信和共享數據、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等組件。
需要再次強調的一點是:與線程不同,進程沒有任何共享狀態,進程修改的數據,改動僅限於該進程內。
Process類的介紹
創建進程的類:
Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動) 強調: 1. 需要使用關鍵字的方式來指定參數 2. args指定的為傳給target函數的位置參數,是一個元組形式,必須有逗號 參數介紹: 1 group參數未使用,值始終為None 2 3 target表示調用對象,即子進程要執行的任務 4 5 args表示調用對象的位置參數元組,args=(1,2,‘egon‘,) 6 7 kwargs表示調用對象的字典,kwargs={‘name‘:‘egon‘,‘age‘:18} 8 9 name為子進程的名稱
方法介紹:
1 p.start():啟動進程,並調用該子進程中的p.run() 2 p.run():進程啟動時運行的方法,正是它去調用target指定的函數,我們自定義類的類中一定要實現該方法 3 4 p.terminate():強制終止進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵屍進程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那麽也將不會被釋放,進而導致死鎖 5 p.is_alive():如果p仍然運行,返回True 6 7 p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,需要強調的是,p.join只能join住start開啟的進程,而不能join住run開啟的進程
屬性介紹:
1 p.daemon:默認值為False,如果設為True,代表p為後臺運行的守護進程,當p的父進程終止時,p也隨之終止,並且設定為True後,p不能創建自己的新進程,必須在p.start()之前設置 2 3 p.name:進程的名稱 4 5 p.pid:進程的pid 6 7 p.exitcode:進程在運行時為None、如果為–N,表示被信號N結束(了解即可) 8 9 p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是為涉及網絡連接的底層進程間通信提供安全性,這類連接只有在具有相同的身份驗證鍵時才能成功(了解即可)
Process類的使用
=====================part1:創建並開啟子進程的兩種方式
註意:在windows中Process()必須放到# if __name__ == ‘__main__‘:下
Since Windows has no fork, the multiprocessing module starts a new Python process and imports the calling module.
If Process() gets called upon import, then this sets off an infinite succession of new processes (or until your machine runs out of resources).
This is the reason for hiding calls to Process() inside
if __name__ == "__main__"
since statements inside this if-statement will not get called upon import.
由於Windows沒有fork,多處理模塊啟動一個新的Python進程並導入調用模塊。
如果在導入時調用Process(),那麽這將啟動無限繼承的新進程(或直到機器耗盡資源)。
這是隱藏對Process()內部調用的原,使用if __name__ == “__main __”,這個if語句中的語句將不會在導入時被調用。
#開進程的方法一: import time import random from multiprocessing import Process def piao(name): print(‘%s piaoing‘ %name) time.sleep(random.randrange(1,5)) print(‘%s piao end‘ %name) p1=Process(target=piao,args=(‘egon‘,)) #必須加,號 p2=Process(target=piao,args=(‘alex‘,)) p3=Process(target=piao,args=(‘wupeqi‘,)) p4=Process(target=piao,args=(‘yuanhao‘,)) p1.start() p2.start() p3.start() p4.start() print(‘主線程‘)開進程的方法一:
#開進程的方法二: import time import random from multiprocessing import Process class Piao(Process): def __init__(self,name): super().__init__() self.name=name def run(self): print(‘%s piaoing‘ %self.name) time.sleep(random.randrange(1,5)) print(‘%s piao end‘ %self.name) p1=Piao(‘egon‘) p2=Piao(‘alex‘) p3=Piao(‘wupeiqi‘) p4=Piao(‘yuanhao‘) p1.start() #start會自動調用run p2.start() p3.start() p4.start() print(‘主線程‘)開進程的方法二
練習一:把上周學的socket通信變成並發的形式
from socket import * from multiprocessing import Process server=socket(AF_INET,SOCK_STREAM) server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) server.bind((‘127.0.0.1‘,8080)) server.listen(5) def talk(conn,client_addr): while True: try: msg=conn.recv(1024) if not msg:break conn.send(msg.upper()) except Exception: break if __name__ == ‘__main__‘: #windows下start進程一定要寫到這下面 while True: conn,client_addr=server.accept() p=Process(target=talk,args=(conn,client_addr)) p.start()server端
from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect((‘127.0.0.1‘,8080)) while True: msg=input(‘>>: ‘).strip() if not msg:continue client.send(msg.encode(‘utf-8‘)) msg=client.recv(1024) print(msg.decode(‘utf-8‘))多個client端
每來一個客戶端,都在服務端開啟一個進程,如果並發來一個萬個客戶端,要開啟一萬個進程嗎,你自己嘗試著在你自己的機器上開啟一萬個,10萬個進程試一試。 解決方法:進程池
9 異常處理 操作系統 進程線程 隊列+生產消費者模型 進程同步 回調函數