Python是運行在解釋器中的語言,查找資料知道,python中有一個全局鎖(GIL),在使用多進程(Thread)的狀況下,不能發揮多核的優點。而使用多進程(Multiprocess),則能夠發揮多核的優點真正地提升效率。
若是多線程的進程是CPU密集型的,那多線程並不能有多少效率上的提高,相反還可能會由於線程的頻繁切換,致使效率降低,推薦使用多進程;若是是IO密集型,多線程的進程能夠利用IO阻塞等待時的空閒時間執行其餘線程,提高效率。python
1.Linux建立子進程的原理:
1). 父進程和子進程, 若是父進程結束, 子進程也隨之結束;
2). 先有父進程, 再有子進程, 經過fork函數實現;linux
2.fork函數的返回值:調用該方法一次, 返回兩次;bootstrap
3.Window也能使用fork函數麼?網絡
Windows沒有fork函數, Mac有fork函數(Unix -> Linux, Unix-> Mac), 封裝了一個模塊multiprocessing
4.經常使用方法:數據結構
import os import time print("當前進程(pid=%d)正在運行..." %(os.getpid())) print("當前進程的父進程(pid=%d)正在運行..." %(os.getppid())) print("正在建立子進程......") pid = os.fork() pid2 = os.fork() print("第1個:", pid) print("第2個: ", pid2) if pid == 0: print("這是建立的子進程, 子進程的id爲%s, 父進程的id爲%s" %(os.getpid(), os.getppid())) else: print("當前是父進程[%s]的返回值%s" %(os.getpid(), pid)) time.sleep(100)
在win系統下,使用實例化multiprocessing.Process建立進程須添加'if __name__=="__main__"',不然會出現如下報錯:多線程
RuntimeError:app
An attempt has been made to start a new process before the current process has finished its bootstrapping phase. This probably means that you are not using fork to start your child processes and you have forgotten to use the proper idiom in the main module: if __name__ == '__main__': freeze_support() ... The "freeze_support()" line can be omitted if the program is not going to be frozen to produce an executable.
import multiprocessing def job(): print("當前子進程的名稱爲%s" %(multiprocessing.current_process())) if __name__=="__main__": #win操做系統須要加上,不然會出現異常報錯RuntimeError # 建立一個進程對象(group=None, target=None, name=None, args=(), kwargs={}) p1 = multiprocessing.Process(target=job) p2 = multiprocessing.Process(target=job) # 運行多進程, 執行任務 p1.start() p2.start() # 等待全部的子進程執行結束, 再執行主進程的內容 p1.join() p2.join() print("任務執行結束.......")
from multiprocessing import Process import multiprocessing class JobProcess(Process): # 重寫Process的構造方法, 獲取新的屬性 def __init__(self,queue): super(JobProcess, self).__init__() self.queue = queue # 重寫run方法, 將執行的任務放在裏面便可 def run(self): print("當前進程信息%s" %(multiprocessing.current_process())) if __name__=="__main__": processes = [] # 啓動10個子進程, 來處理須要執行的任務; for i in range(10): #示例化類,建立進程 p = JobProcess(queue=3) processes.append(p) #啓動多進程,執行任務 p.start() #等待全部的子進程結束,再執行主進程 [pro.join() for pro in processes] print("任務執行結束")
守護線程:函數
setDeamon: True: 主線程執行結束, 子線程再也不繼續執行; Flase:
守護進程:測試
setDeamon: True: 主進程執行結束, 子進程再也不繼續執行; Flase:
import multiprocessing import time def deamon(): #守護進程:當主程序運行結束,子進程也結束 name = multiprocessing.current_process() print("%s開始執行" %(name)) time.sleep(3) print("執行結束") if __name__=="__main__": p1 = multiprocessing.Process(target=deamon,name='hello') p1.daemon = True p1.start() time.sleep(2) print("整個程序執行結束")
有些進程或許再執行死循環任務,此時咱們手動結束進程
terminate()spa
import multiprocessing import time def job(): name = multiprocessing.current_process() print("%s進程開啓" %(name)) time.sleep(3) print("進程結束") if __name__=="__main__": p = multiprocessing.Process(target=job) print("進程開啓:",p.is_alive()) p.start() print("進程開啓:",p.is_alive()) p.terminate() print("進程開啓:",p.is_alive()) time.sleep(0.001) print("進程開啓:",p.is_alive()) print("程序執行結束")
計算密集型任務的特色是要進行大量的計算, 消耗CPU資源, 好比計算圓周率、 對視頻進行高清解碼等等, 全靠CPU的運算能力。 這種計算密集型任務雖然也能夠用多任務完成, 可是任務越多, 花在任務切換的時間就越多, CPU執行任務的效率就越低, 因此, 要最高效地利用CPU, 計算密集型任務同時進行的數量應當等於CPU的核心數。計算密集型任務因爲主要消耗CPU資源, 所以, 代碼運行效率相當重要。 Python這樣的腳本語言運行效率很低, 徹底不適合計算密集型任務。 對於計算密集型任務,最好用C語言編寫。
第二種任務的類型是IO密集型, 涉及到網絡、 磁盤IO的任務都是IO密集型任務, 這類任務的特色是CPU消耗不多, 任務的大部分時間都在等待IO操做完成(由於IO的速度遠遠低於CPU和內存的速度)。對於IO密集型任務, 任務越多, CPU效率越高, 但也有一個限度。 常見的大部分任務都是IO密集型任務, 好比Web應用。
多進程模式最大的優勢就是穩定性高, 由於一個子進程崩潰了, 不會影響主進程和其餘子進程。(固然主進程掛了全部進程就全掛了, 可是Master進程只負責分配任務, 掛掉的機率低)著名的Apache最先就是採用多進程模式。
多進程模式的缺點是建立進程的代價大, 在Unix/Linux系統下, 用 fork 調用還行, 在Windows下建立進程開銷巨大。 另外, 操做系統能同時運行的進程數也是有限的, 在內存和。CPU的限制下, 若是有幾千個進程同時運行, 操做系統連調度都會成問題。
多線程模式一般比多進程快一點, 可是也快不到哪去, 並且, 多線程模式致命的缺點就是任何一個線程掛掉均可能直接形成整個進程崩潰, 由於全部線程共享進程的內存。 在Windows上, 若是一個線程執行的代碼出了問題, 你常常能夠看到這樣的提示:「該程序執行了非法操做, 即將關閉」, 其實每每是某個線程出了問題, 可是操做系統會強制結束整個進程。
這裏經過一個計算密集型任務,來測試多進程和多線程的執行效率。
import multiprocessing import threading from mytimeit import timeit class JobProcess(multiprocessing.Process): def __init__(self,li): super(JobProcess, self).__init__() self.li = li def run(self): for i in self.li: sum(i) class JobThread(threading.Thread): def __init__(self,li): super(JobThread, self).__init__() self.li = li def run(self): for i in self.li: sum(i) @timeit def many_processs(): li = [[24892,23892348,239293,233],[2382394,49230,2321234],[48294,28420,29489]]*10 processes = [] for i in li : p = JobProcess(li) processes.append(p) p.start() [pro.join() for pro in processes] print("多進程執行任務結束,✌") @timeit def many_thread(): #建立進程和銷燬進程是時間的,若是li長度不夠,會形成多線程快過多進程 li = [[24892,23892348,239293,233],[2382394,49230,2321234],[48294,28420,29489]]*1000 threads = [] for i in li : t = JobThread(li) threads.append(t) t.start() [thread.join() for thread in threads] print("多線程執行任務結束,✌") if __name__ =="__main__": many_processs() many_thread()
演示了生產者和消費者的場景。生產者生產貨物,而後把貨物放到一個隊列之類的數據結構中,生產貨物所要花費的時間沒法預先肯定。消費者消耗生產者生產的貨物的時間也是不肯定的。
經過隊列來實現進程間的通訊
import multiprocessing import threading from multiprocessing import Queue class Producer(multiprocessing.Process): def __init__(self,queue): super(Producer, self).__init__() self.queue = queue def run(self): for i in range(13): #往隊列添加內容 self.queue.put(i) print("生產者傳遞的消息爲%s" %(i)) return self.queue class Consumer(multiprocessing.Process): def __init__(self,queue): super(Consumer, self).__init__() self.queue = queue def run(self): #獲取隊列內容 #get會自動判斷隊列是否爲空,若是是空, 跳出循環, 不會再去從隊列獲取數據; while True: print("進程獲取消息爲:%s" %(self.queue.get())) if __name__=="__main__": queue = Queue(maxsize=100) p = Producer(queue) p.start() c = Consumer(queue) c.start() p.join() c.join(2) c.terminate() #終止進程 print("進程間通訊結束,( •̀ ω •́ )y")