python之多進程
一.概述
1.什麽是進程
進程:正在進行的一個過程或者說一個任務。而負責執行任務則是cpu。
2.進程與程序區別
程序僅僅只是一堆代碼而已,而進程指的是程序的運行過程。
3.並發與並行
無論是並行還是並發,在用戶看來都是‘同時‘運行的,不管是進程還是線程,都只是一個任務而已,真是幹活的是cpu,cpu來做這些任務,而一個cpu同一時刻只能執行一個任務。
並發:是偽並行,即看起來是同時運行。單個cpu+多道技術就可以實現並發。
並行:同時運行,只有具備多個cpu才能實現並行。
4.進程的層次結構
無論UNIX還是windows,進程只有一個父進程,不同的是:
在UNIX中所有的進程,都是以init進程為根,組成樹形結構。父子進程共同組成一個進程組,這樣,當從鍵盤發出一個信號時,該信號被送給當前與鍵盤相關的進程組中的所有成員。
在windows中,沒有進程層次的概念,所有的進程都是地位相同的,唯一類似於進程層次的暗示,是在創建進程時,父進程得到一個特別的令牌(稱為句柄),該句柄可以用來控制子進程,但是父進程有權把該句柄傳給其他子進程,這樣就沒有層次了。
5.進程的狀態
在兩種情況下會導致一個進程在邏輯上不能運行:
1.進程掛起是自身原因,遇到I/O阻塞,便要讓出CPU讓其他進程去執行,這樣保證CPU一直在工作。
2.與進程無關,是操作系統層面,可能會因為一個進程占用時間過多,或者優先級等原因,而調用其他的進程去使用CPU。
因而一個進程由三種狀態
二.進程的使用
1.multiprocessing模塊介紹
python中的多線程無法利用多核優勢,如果想要充分地使用多核CPU的資源(os.cpu\_count\(\)查看),在python中大部分情況需要使用多進程。
Python提供了multiprocessing。 multiprocessing模塊用來開啟子進程,並在子進程中執行我們定制的任務(比如函數),該模塊與多線程模塊threading的編程接口類似。multiprocessing模塊的功能眾多:支持子進程、通信和共享數據、執行不同形式的同步,>提供了Process、Queue、Pipe、Lock等組件。
需要再次強調的一點是:與線程不同,進程沒有任何共享狀態,進程修改的數據,改動僅限於該進程內。
2.process類介紹
創建進程的類:
Process([group [, target [, name [, args [, kwargs]]]]])
由該類實例化得到的對象,可用來開啟一個子進程
強調: 1. 需要使用關鍵字的方式來指定參數
2. args指定的為傳給target函數的位置參數,是一個元組形式,必須有逗號
參數介紹
group參數未使用,值始終為None target表示調用對象,即子進程要執行的任務 args表示調用對象的位置參數元組,args=(1,2,‘egon‘,) kwargs表示調用對象的字典,kwargs={‘name‘:‘egon‘,‘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.daemon:默認值為False,如果設為True,代表p為後臺運行的守護進程,當p的父進程終止時,p也隨之終止,並且設定為True後,p不能創建自己的新進程,必須在p.start()之前設置
p.name:進程的名稱
p.pid:進程的pid
3.開啟進程的兩個方法
方法一
from multiprocessing import Process import time def task(name): print(‘%s is running‘ %name) time.sleep(3) print(‘%s is done‘ %name) if __name__ == ‘__main__‘: # Process(target=task, kwargs={‘name‘:‘子進程1‘}) p = Process(target=task, args=(‘子進程1‘,)) p.start() # 僅僅只是發送一個信號 print(‘主‘)
方法二
from multiprocessing import Process import time class MyProcss(Process): def __init__(self, name): super().__init__() self.name = name def run(self): print(‘%s is running‘ % self.name) time.sleep(3) print(‘%s is done‘ % self.name) if __name__ == ‘__main__‘: p = MyProcss(‘子進程1‘) p.start() print(‘主‘)
4.join方法和守護進程
a.join
在主進程運行過程中如果想並發地執行其他的任務,我們可以開啟子進程,此時主進程的任務與子進程的任務分兩種情況。
情況一:在主進程的任務與子進程的任務彼此獨立的情況下,主進程的任務先執行完畢後,主進程還需要等待子進程執行完畢,然後統一回收資源。
情況二:如果主進程的任務在執行到某一個階段時,需要等待子進程執行完畢後才能繼續執行,就需要有一種機制能夠讓主進程檢測子進程是否運行完畢,在子進程執行完畢後才繼續執行,否則一直在原地阻塞,這就是join方法的作用。
from multiprocessing import Process import time,os def task(name): print(‘%s is running,parent id is %s‘ % (os.getpid(), os.getppid())) time.sleep(3) print(‘%s is done,parent id is %s‘ % (os.getpid(), os.getppid())) if __name__ == ‘__main__‘: p = Process(target=task, args=(‘子進程1‘,)) p.start() # 僅僅只是發送一個信號 p.join() # 等待子進程結束 print(‘主‘, os.getpid(), os.getppid())
b.守護進程
主進程創建子進程,然後將該進程設置成守護自己的進程,守護進程就好比崇禎皇帝身邊的老太監,崇禎皇帝已死老太監就跟著殉葬了。
關於守護進程需要強調兩點:
其一:守護進程會在主進程代碼執行結束後就終止
其二:守護進程內無法再開啟子進程,否則拋出異常:AssertionError: daemonic processes are not allowed to have children
適用場景:
如果我們有兩個任務需要並發執行,那麽開一個主進程和一個子進程分別去執行就ok了,如果子進程的任務在主進程任務結束後就沒有存在的必要了,那麽該子進程應該在開啟前就被設置成守護進程。主進程代碼運行結束,守護進程隨即終止。
from multiprocessing import Process import time def task(name): print(‘%s is running‘ % name) time.sleep(2) if __name__ == ‘__main__‘: p = Process(target=task, args=(‘子進程1‘,)) p.daemon = True # 子進程還沒啟動就隨主關閉 p.start() print(‘主‘)
5.互斥鎖
進程之間數據不共享,但是共享同一套文件系統,所以訪問同一個文件,或同一個打印終端,是沒有問題的,而共享帶來的是競爭,競爭帶來的結果就是錯亂,如下代碼:
#並發運行,效率高,但競爭同一打印終端,帶來了打印錯亂 from multiprocessing import Process import os,time
def work(): print(‘%s is running‘ %os.getpid()) time.sleep(2) print(‘%s is done‘ %os.getpid())
if __name__ == ‘__main__‘: for i in range(3): p=Process(target=work) p.start()
加鎖處理來控制輸出
#由並發變成了串行,犧牲了運行效率,但避免了競爭 from multiprocessing import Process,Lock import os,time
def work(lock): lock.acquire() #加鎖 print(‘%s is running‘ %os.getpid()) time.sleep(2) print(‘%s is done‘ %os.getpid()) lock.release() #釋放鎖
if __name__ == ‘__main__‘: lock=Lock() for i in range(3): p=Process(target=work,args=(lock,)) p.start()
6.隊列
進程彼此之間互相隔離,要實現進程間通信(IPC),multiprocessing模塊支持兩種形式:隊列和管道,這兩種方式都是使用消息傳遞的。
from multiprocessing import Queue q = Queue(3) q.put(‘hello‘) q.put({‘a‘: 1}) q.put([3, 3, 3]) print(q.full()) # q.put(4) #再放就阻塞住了 print(q.get()) print(q.get()) print(q.get()) print(q.empty()) # 空了 # print(q.get()) #再取就阻塞住了
python之多進程