python_fullstack基础(十八)-并发编程
并發編程
一、進程
1、理論背景
①操作系統背景
- 手工操作—穿孔卡片?
原理:用戶獨占計算機,CPU等待手工操作,資源利用和CPU利用效率極低 - 批處理—磁帶處理、批處理系統?
原理:主機與輸入機之間增加磁帶存儲設備,使得計算機可以自動將磁帶讀入計算機,成批完成作業?
分類:脫機批處理系統、聯機批處理系統 - 多道程序系統?
原理:指允許多個程序同時進入內存并交替運行?
特點:宏觀上并行、微觀上串行 - 分時系統?
原理:把處理機的運行時間分成很短的時間片,按時間片輪流把處理機分配給各聯機作業使用?
特點:多路性、交互性、獨立性、及時性;在進行I/O切換的時候實際上效率相比下降低了 - 實時系統?
原理:程序獨占CPU,為了讓程序能夠及時響應隨機發生的外部事件,并在嚴格的時間范圍內完成對該事件的處理?
分類:實時控制系統、實時信息處理系統?
特點:及時響應、高可靠性。 - 通用操作系統?
定義:具有多種類型操作特征的操作系統。可以同時兼有多道批處理、分時、實時處理的功能,或其中兩種以上的功能。 - 操作系統的進一步發展?
分類:個人計算機操作系統、網絡操作系統、分布式操作系統 - 操作系統的作用?
作用:?- 1、隱藏了丑陋的硬件調用接口,為應用程序員提供調用硬件資源的更好,更簡單,更清晰的模型(系統調用接口)
- 2、將應用程序對硬件資源的競態請求變得有序化
- 手工操作—穿孔卡片?
②何為進程、何為進程調度
-
進程
進程的特征:
動態性:進程的實質是程序在多道程序系統中的一次執行過程,進程是動態產生,動態消亡的。 并發性:任何進程都可以同其他進程一起并發執行 獨立性:進程是一個能獨立運行的基本單位,同時也是系統分配資源和調度的獨立單位; 異步性:由于進程間的相互制約,使進程具有執行的間斷性,即進程按各自獨立的、不可預知的速度向前推進 結構特征:進程由程序、數據和進程控制塊三部分組成。 多個不同的進程可以包含相同的程序:一個程序在不同的數據集里就構成不同的進程,能得到不同的結果;但是執行過程中,程序不能發生改變。
進程與程序的區別:
程序是指令和數據的有序集合,其本身沒有任何運行的含義,是一個靜態的概念。 而進程是程序在處理機上的一次執行過程,它是一個動態的概念。 程序可以作為一種軟件資料長期存在,而進程是有一定生命期的。 程序是永久的,進程是暫時的。?進程調度
-
先來先服務調度算法:?
先來先服務(FCFS)調度算法是一種最簡單的調度算法,該算法既可用于作業調度,也可用于進程調度。FCFS算法比較有利于長作業(進程),而不利于短作業(進程)。由此可知,本算法適合于CPU繁忙型作業,而不利于I/O繁忙型的作業(進程)。 -
短作業優先調度算法:?
短作業(進程)優先調度算法(SJ/PF)是指對短作業或短進程優先調度的算法,該算法既可用于作業調度,也可用于進程調度。但其對長作業不利;不能保證緊迫性作業(進程)被及時處理;作業的長短只是被估算出來的。 -
時間片輪轉法:?
時間片輪轉(Round Robin,RR)法的基本思路是讓每個進程在就緒隊列中的等待時間與享受服務的時間成比例。在時間片輪轉法中,需要將CPU的處理時間分成固定大小的時間片,如果一個進程在被調度選中之后用完了系統規定的時間片,但又未完成要求的任務,則它自行釋放自己所占有的CPU而排到就緒隊列的末尾,等待下一次調度。同時,進程調度程序又去調度當前就緒隊列中的第一個進程。 -
多級反饋隊列:?
應設置多個就緒隊列,并為各個隊列賦予不同的優先級。當一個新進程進入內存后,首先將它放入第一隊列的末尾,按FCFS原則排隊等待調度。當輪到該進程執行時,如它能在該時間片內完成,便可準備撤離系統;如果它在一個時間片結束時尚未完成,調度程序便將該進程轉入第二隊列的末尾按照FCFS原則等待調度執行,以此類推,而且僅當第一隊列空閑時,調度程序才調度第二隊列中的進程運行
-
③進程的并發、并行
- 并行:是指兩者同時執行
- 并發:是指資源有限的情況下,兩者交替輪流使用資源,以便提高效率
- 區別:并行是從微觀上,也就是在一個精確的時間片刻,有不同的程序在執行,這就要求必須有多個處理器;并發是從宏觀上,在一個時間段上可以看出是同時執行的,比如一個服務器同時處理多個session。?
?
④同步、異步、阻塞、非阻塞
-
同步:?
系統功能調用時,在沒有得到結果之前,該調用就不會返回。按照這個定義,其實絕大多數函數都是同步調用。但是一般而言,我們在說同步、異步的時候,特指那些需要其他部件協作或者需要一定時間完成的任務。 -
異步:?
異步的概念和同步相對。當一個異步功能調用發出后,調用者不能立刻得到結果。當該異步功能完成后,通過狀態、通知或回調來通知調用者。如果異步功能用狀態來通知,那么調用者就需要每隔一定時間檢查一次,效率就很低(有些初學多線程編程的人,總喜歡用一個循環去檢查某個變量的值,這其實是一 種很嚴重的錯誤)。如果是使用通知的方式,效率則很高,因為異步功能幾乎不需要做額外的操作。至于回調函數,其實和通知沒太多區別。 -
阻塞:?
阻塞調用是指調用結果返回之前,當前線程會被掛起(如遇到io操作)。函數只有在得到結果之后才會將阻塞的線程激活。有人也許會把阻塞調用和同步調用等同起來,實際上他是不同的。對于同步調用來說,很多時候當前線程還是激活的,只是從邏輯上當前函數沒有返回而已。 -
非阻塞:?
非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前也會立刻返回,同時該函數不會阻塞當前線程。 -
總結:
- 同步與異步針對的是函數/任務的調用方式:同步就是當一個進程發起一個函數(任務)調用的時候,一直等到函數(任務)完成,而進程繼續處于激活狀態。而異步情況下是當一個進程發起一個函數(任務)調用的時候,不會等函數返回,而是繼續往下執行當,函數返回的時候通過狀態、通知、事件等方式通知進程任務完成。
- 阻塞與非阻塞針對的是進程或線程:阻塞是當請求不能滿足的時候就將進程掛起,而非阻塞則不會阻塞當前進程
-
同步阻塞形式:?
效率最低。一直等待進程結束,其他什么也不做 -
異步阻塞形式:?
異步操作是可以被阻塞住的,只不過它不是在處理消息時阻塞,而是在等待消息通知時被阻塞 -
同步非阻塞形式:?
實際上是效率低下的,因為程序需要在這兩種不同的行為之間來回的切換,效率浪費在了程序切換中 -
異步非阻塞形式:?
效率較高,程序沒有在兩種不同的操作中來回切換 -
總結:?
同步和阻塞混淆很容易混淆,因為很多時候同步操作會以阻塞的形式表現出來,同樣很多人也會把異步和非阻塞混淆,因為異步操作一般都不會在真正的I/O操作處被阻塞。
⑤進程的創建、結束(了解)
-
進程的創建的4種形式:
- 系統初始化(查看進程linux中用ps命令,windows中用任務管理器,前臺進程負責與用戶交互,后臺運行的進程與用戶無關,運行在后臺并且只在需要時才喚醒的進程,稱為守護進程,如電子郵件、web頁面、新聞、打印)
- 一個進程在運行過程中開啟了子進程(如nginx開啟多進程,os.fork,subprocess.Popen等)
- 用戶的交互式請求,而創建一個新進程(用戶雙擊操作)
- 一個批處理作業的初始化(只在大型機的批處理系統中應用)
-
進程的結束
- 正常退出(自愿,如用戶點擊交互式頁面的叉號,或程序執行完畢調用發起系統調用正常退出,在linux中用exit,在windows中用ExitProcess)
- 出錯退出(自愿,python a.py中a.py不存在)
- 嚴重錯誤(非自愿,執行非法指令,如引用不存在的內存,I/O等,可以捕捉異常,try…except…)
- 被其他進程殺死(非自愿,如kill -9 PID)
2、python中的進程應用——multiprocessing模塊
①multiprocessing模塊簡介:
multiprocess不是一個模塊而是python中一個操作、管理進程的包,這個包中幾乎包含了和進程有關的所有子模塊。以下將分為:進程創建部分、進程同步部分、進程間通信部分、進程池部分和進程之間數據共享來介紹
②進程創建部分
-
process模塊語法簡介?
Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動)-
強調:
-
-
參數介紹:
group參數未使用,值始終為None target表示調用對象,即子進程要執行的任務 args表示調用對象的位置參數元組,args=(1,2,’yang’,) kwargs表示調用對象的字典,kwargs={‘name’:’yang’,’age’:18} name為子進程的名稱子進程常用方法
p.start():啟動進程,并調用該子進程中的p.run() p.run():進程啟動時運行的方法,正是它去調用target指定的函數,我們自定義類的類中一定要實現該方法 p.terminate():強制終止進程p,不會進行任何清理操作,如果p創建了子進程,該子進程就成了僵尸進程,使用該方法需要特別小心這種情況。如果p還保存了一個鎖那么也將不會被釋放,進而導致死鎖 p.is_alive():如果p仍然運行,返回True p.join([timeout]):主線程等待p終止(強調:是主線程處于等的狀態,而p是處于運行的狀態)。timeout是可選的超時時間,需要強調的是,p.join只能join住start開啟的進程,而不能join住run開啟的進程 p.daemon:默認值為False,如果設為True,代表p為后臺運行的守護進程,當p的父進程終止時,p也隨之終止,并且設定為True后,p不能創建自己的新進程,必須在p.start()之前設置 p.name:進程的名稱 p.pid:進程的pid p.exitcode:進程在運行時為None、如果為–N,表示被信號N結束(了解即可) p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是為涉及網絡連接的底層進程間通信提供安全性,這類連接只有在具有相同的身份驗證鍵時才能成功(了解即可)-
tips?
在Windows操作系統中由于沒有fork(linux操作系統中創建進程的機制),在創建子進程的時候會自動 import 啟動它的這個文件,而在import的時候又執行了整個文件。因此如果將process()直接寫在文件中就會無限遞歸創建子進程報錯。所以必須把創建子進程的部分使用if __name__ ==‘__main__’ 判斷保護起來,import 的時候,就不會遞歸運行了。 -
面向函數——創建子進程實現并發
-
- 面向函數——創建多個子進程
- 面向函數——多個進程同時運行
- 面向對象——創建進程
- 進程間數據隔離問題
- 守護進程:會隨著主進程的結束而結束?
主進程創建守護進程?
其一:守護進程會在主進程代碼執行結束后就終止?
其二:守護進程內無法再開啟子進程,否則拋出AssertionError異常?
注意:進程之間是互相獨立的,主進程代碼運行結束,守護進程隨即終止
③進程同步部分(multiprocess.Lock、multiprocess.Semaphore、multiprocess.Event)
-
鎖multiprocess.Lock
-
加鎖可以保證多個進程修改同一塊數據時,同時刻只能有一個任務可以進行修改,即串行的修改,這樣做雖然犧牲了速度卻保證了數據安全。所以可以mutiprocessing模塊為我們提供更好的的基于消息的IPC通信機制:隊列和管道
-
應用實例
-
-
信號量multiprocess.Semaphore
-
介紹:互斥鎖同時只允許一個線程更改數據,而信號量Semaphore是同時允許一定數量的線程更改數據
-
注意:信號量同步基于內部計數器,每調用一次acquire(),計數器減1;每調用一次release(),計數器加1;當計數器為0時,acquire()調用被阻塞。這是迪科斯徹(Dijkstra)信號量概念P()和V()的Python實現。信號量同步機制適用于訪問像服務器這樣的有限資源。信號量與進程池的概念很像,但是要區分開,信號量涉及到加鎖的概念。
-
應用實例
事件multiprocess.Event
-
介紹:
event.wait 方法時就會阻塞,如果“Flag”值為True,那么event.wait 方法時便不再阻塞。 clear:將“Flag”設置為False set:將“Flag”設置為True
應用實例
def traffic_light(e):while True:if e.is_set():time.sleep(1)print('紅燈亮')e.clear()else:time.sleep(1)print('綠燈亮')e.set()def car(i, e):e.wait()print('{}:車通過...'.format(i))if __name__ == '__main__':e = Event()traffic = Process(target=traffic_light, args=(e,))traffic.daemon = Truetraffic.start()for i in range(30):if i % 6:time.sleep(random.random())car_obj = Process(target=car, args=(i, e))car_obj.start()④進程間通信部分(multiprocess.Queue、multiprocess.Pipe)
- 進程間通信:IPC(Inter-Process Communication)
-
隊列multiprocess.Queue
- 介紹:創建共享的進程隊列,Queue是多進程安全的隊列,可以使用Queue實現多進程之間的數據傳遞。
-
語法:
- Queue([maxsize]) :創建共享的進程隊列參數 :maxsize是隊列中允許的最大項數。如果省略此參數,則無大小限制。(底層隊列使用管道和鎖定實現)
?
Queue的實例q具有以下方法:
q.get( [ block [ ,timeout ] ] )返回q中的一個項目。如果q為空,此方法將阻塞,直到隊列中有項目可用為止。block用于控制阻塞行為,默認為True. 如果設置為False,將引發Queue.Empty異常(定義在Queue模塊中)。timeout是可選超時時間,用在阻塞模式中。如果在制定的時間間隔內沒有項目變為可用,將引發Queue.Empty異常。 q.get_nowait( )同q.get(False)方法。 q.put(item [, block [,timeout ] ] )將item放入隊列。如果隊列已滿,此方法將阻塞至有空間可用為止。block控制阻塞行為,默認為True。如果設置為False,將引發Queue.Empty異常(定義在Queue庫模塊中)。timeout指定在阻塞模式中等待可用空間的時間長短。超時后將引發Queue.Full異常。 q.qsize()返回隊列中目前項目的正確數量。此函數的結果并不可靠,因為在返回結果和在稍后程序中使用結果之間,隊列中可能添加或刪除了項目。在某些系統上,此方法可能引發NotImplementedError異常。 q.empty()如果調用此方法時 q為空,返回True。如果其他進程或線程正在往隊列中添加項目,結果是不可靠的。也就是說,在返回和使用結果之間,隊列中可能已經加入新的項目。 q.full()如果q已滿,返回為True. 由于線程的存在,結果也可能是不可靠的(參考q.empty()方法)-
管道multiprocess.Pipe
⑤進程池部分
⑥進程之間數據共享
?轉載于:https://www.cnblogs.com/lidaxu/p/8407011.html
總結
以上是生活随笔為你收集整理的python_fullstack基础(十八)-并发编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL数据库恢复(使用mysqlbi
- 下一篇: 获取程序执行的时间