深入理解python多進(jìn)程編程
1、python多進(jìn)程編程背景
python中的多進(jìn)程最大的好處就是充分利用多核cpu的資源,不像python中的多線程,受制于GIL的限制,從而只能進(jìn)行cpu分配,在python的多進(jìn)程中,適合于所有的場合,基本上能用多線程的,那么基本上就能用多進(jìn)程。
在進(jìn)行多進(jìn)程編程的時(shí)候,其實(shí)和多線程差不多,在多線程的包threading中,存在一個(gè)線程類Thread,在其中有三種方法來創(chuàng)建一個(gè)線程,啟動線程,其實(shí)在多進(jìn)程編程中,存在一個(gè)進(jìn)程類Process,也可以使用那集中方法來使用;在多線程中,內(nèi)存中的數(shù)據(jù)是可以直接共享的,例如list等,但是在多進(jìn)程中,內(nèi)存數(shù)據(jù)是不能共享的,從而需要用單獨(dú)的數(shù)據(jù)結(jié)構(gòu)來處理共享的數(shù)據(jù);在多線程中,數(shù)據(jù)共享,要保證數(shù)據(jù)的正確性,從而必須要有所,但是在多進(jìn)程中,鎖的考慮應(yīng)該很少,因?yàn)檫M(jìn)程是不共享內(nèi)存信息的,進(jìn)程之間的交互數(shù)據(jù)必須要通過特殊的數(shù)據(jù)結(jié)構(gòu),在多進(jìn)程中,主要的內(nèi)容如下圖:
2、多進(jìn)程的類Process
多進(jìn)程的類Process和多線程的類Thread差不多的方法,兩者的接口基本相同,具體看以下的代碼:
#!/usr/bin/env python from multiprocessing import Process import os import time def func(name): print 'start a process' time.sleep(3) print 'the process parent id :',os.getppid() print 'the process id is :',os.getpid() if __name__ =='__main__': processes = [] for i in range(2): p = Process(target=func,args=(i,)) processes.append(p) for i in processes: i.start() print 'start all process' for i in processes: i.join() #pass print 'all sub process is done!'
在上面例子中可以看到,多進(jìn)程和多線程的API接口是一樣一樣的,顯示創(chuàng)建進(jìn)程,然后進(jìn)行start開始運(yùn)行,然后join等待進(jìn)程結(jié)束。
在需要執(zhí)行的函數(shù)中,打印出了進(jìn)程的id和pid,從而可以看到父進(jìn)程和子進(jìn)程的id號,在linu中,進(jìn)程主要是使用fork出來的,在創(chuàng)建進(jìn)程的時(shí)候可以查詢到父進(jìn)程和子進(jìn)程的id號,而在多線程中是無法找到線程的id,執(zhí)行效果如下:
start all process start a process start a process the process parent id : 8036 the process parent id : 8036 the process id is : 8037 the process id is : 8038 all sub process is done!
在操作系統(tǒng)中查詢的id的時(shí)候,最好用pstree,清晰:
├─sshd(1508)─┬─sshd(2259)───bash(2261)───python(7520)─┬─python(7521) │ │ ├─python(7522) │ │ ├─python(7523) │ │ ├─python(7524) │ │ ├─python(7525) │ │ ├─python(7526) │ │ ├─python(7527) │ │ ├─python(7528) │ │ ├─python(7529) │ │ ├─python(7530) │ │ ├─python(7531) │ │ └─python(7532)
在進(jìn)行運(yùn)行的時(shí)候,可以看到,如果沒有join語句,那么主進(jìn)程是不會等待子進(jìn)程結(jié)束的,是一直會執(zhí)行下去,然后再等待子進(jìn)程的執(zhí)行。
在多進(jìn)程的時(shí)候,說,我怎么得到多進(jìn)程的返回值呢?然后寫了下面的代碼:
#!/usr/bin/env python import multiprocessing class MyProcess(multiprocessing.Process): def __init__(self,name,func,args): super(MyProcess,self).__init__() self.name = name self.func = func self.args = args self.res = '' def run(self): self.res = self.func(*self.args) print self.name print self.res return (self.res,'kel') def func(name): print 'start process...' return name.upper() if __name__ == '__main__': processes = [] result = [] for i in range(3): p = MyProcess('process',func,('kel',)) processes.append(p) for i in processes: i.start() for i in processes: i.join() for i in processes: result.append(i.res) for i in result: print i
嘗試從結(jié)果中返回值,從而在主進(jìn)程中得到子進(jìn)程的返回值,然而,,,并沒有結(jié)果,后來一想,在進(jìn)程中,進(jìn)程之間是不共享內(nèi)存的 ,那么使用list來存放數(shù)據(jù)顯然是不可行的,進(jìn)程之間的交互必須依賴于特殊的數(shù)據(jù)結(jié)構(gòu),從而以上的代碼僅僅是執(zhí)行進(jìn)程,不能得到進(jìn)程的返回值,但是以上代碼修改為線程,那么是可以得到返回值的。
3、進(jìn)程間的交互Queue
進(jìn)程間交互的時(shí)候,首先就可以使用在多線程里面一樣的Queue結(jié)構(gòu),但是在多進(jìn)程中,必須使用multiprocessing里的Queue,代碼如下:
#!/usr/bin/env python import multiprocessing class MyProcess(multiprocessing.Process): def __init__(self,name,func,args): super(MyProcess,self).__init__() self.name = name self.func = func self.args = args self.res = '' def run(self): self.res = self.func(*self.args) def func(name,q): print 'start process...' q.put(name.upper()) if __name__ == '__main__': processes = [] q = multiprocessing.Queue() for i in range(3): p = MyProcess('process',func,('kel',q)) processes.append(p) for i in processes: i.start() for i in processes: i.join() while q.qsize() > 0: print q.get()
其實(shí)這個(gè)是上面例子的改進(jìn),在其中,并沒有使用什么其他的代碼,主要就是使用Queue來保存數(shù)據(jù),從而可以達(dá)到進(jìn)程間交換數(shù)據(jù)的目的。
在進(jìn)行使用Queue的時(shí)候,其實(shí)用的是socket,感覺,因?yàn)樵谄渲惺褂玫倪€是發(fā)送send,然后是接收recv。
在進(jìn)行數(shù)據(jù)交互的時(shí)候,其實(shí)是父進(jìn)程和所有的子進(jìn)程進(jìn)行數(shù)據(jù)交互,所有的子進(jìn)程之間基本是沒有交互的,除非,但是,也是可以的,例如,每個(gè)進(jìn)程去Queue中取數(shù)據(jù),但是這個(gè)時(shí)候應(yīng)該是要考慮鎖,不然可能會造成數(shù)據(jù)混亂。
4、 進(jìn)程之間交互Pipe
在進(jìn)程之間交互數(shù)據(jù)的時(shí)候還可以使用Pipe,代碼如下:
#!/usr/bin/env python import multiprocessing class MyProcess(multiprocessing.Process): def __init__(self,name,func,args): super(MyProcess,self).__init__() self.name = name self.func = func self.args = args self.res = '' def run(self): self.res = self.func(*self.args) def func(name,q): print 'start process...' child_conn.send(name.upper()) if __name__ == '__main__': processes = [] parent_conn,child_conn = multiprocessing.Pipe() for i in range(3): p = MyProcess('process',func,('kel',child_conn)) processes.append(p) for i in processes: i.start() for i in processes: i.join() for i in processes: print parent_conn.recv()
在以上代碼中,主要是使用Pipe中返回的兩個(gè)socket來進(jìn)行傳輸和接收數(shù)據(jù),在父進(jìn)程中,使用的是parent_conn,在子進(jìn)程中使用的是child_conn,從而子進(jìn)程發(fā)送數(shù)據(jù)的方法send,而在父進(jìn)程中進(jìn)行接收方法recv
最好的地方在于,明確的知道收發(fā)的次數(shù),但是如果某個(gè)出現(xiàn)異常,那么估計(jì)pipe不能使用了。
5、進(jìn)程池pool
其實(shí)在使用多進(jìn)程的時(shí)候,感覺使用pool是最方便的,在多線程中是不存在pool的。
在使用pool的時(shí)候,可以限制每次的進(jìn)程數(shù),也就是剩余的進(jìn)程是在排隊(duì),而只有在設(shè)定的數(shù)量的進(jìn)程在運(yùn)行,在默認(rèn)的情況下,進(jìn)程是cpu的個(gè)數(shù),也就是根據(jù)multiprocessing.cpu_count()得出的結(jié)果。
在poo中,有兩個(gè)方法,一個(gè)是map一個(gè)是imap,其實(shí)這兩方法超級方便,在執(zhí)行結(jié)束之后,可以得到每個(gè)進(jìn)程的返回結(jié)果,但是缺點(diǎn)就是每次的時(shí)候,只能有一個(gè)參數(shù),也就是在執(zhí)行的函數(shù)中,最多是只有一個(gè)參數(shù)的,否則,需要使用組合參數(shù)的方法,代碼如下所示:
#!/usr/bin/env python import multiprocessing def func(name): print 'start process' return name.upper() if __name__ == '__main__': p = multiprocessing.Pool(5) print p.map(func,['kel','smile']) for i in p.imap(func,['kel','smile']): print i
在使用map的時(shí)候,直接返回的一個(gè)是一個(gè)list,從而這個(gè)list也就是函數(shù)執(zhí)行的結(jié)果,而在imap中,返回的是一個(gè)由結(jié)果組成的迭代器,如果需要使用多個(gè)參數(shù)的話,那么估計(jì)需要*args,從而使用參數(shù)args。
在使用apply.async的時(shí)候,可以直接使用多個(gè)參數(shù),如下所示:
#!/usr/bin/env python import multiprocessing import time def func(name): print 'start process' time.sleep(2) return name.upper() if __name__ == '__main__': results = [] p = multiprocessing.Pool(5) for i in range(7): res = p.apply_async(func,args=('kel',)) results.append(res) for i in results: print i.get(2.1)
在進(jìn)行得到各個(gè)結(jié)果的時(shí)候,注意使用了一個(gè)list來進(jìn)行append,要不然在得到結(jié)果get的時(shí)候會阻塞進(jìn)程,從而將多進(jìn)程編程了單進(jìn)程,從而使用了一個(gè)list來存放相關(guān)的結(jié)果,在進(jìn)行得到get數(shù)據(jù)的時(shí)候,可以設(shè)置超時(shí)時(shí)間,也就是get(timeout=5),這種設(shè)置。
總結(jié):
在進(jìn)行多進(jìn)程編程的時(shí)候,注意進(jìn)程之間的交互,在執(zhí)行函數(shù)之后,如何得到執(zhí)行函數(shù)的結(jié)果,可以使用特殊的數(shù)據(jù)結(jié)構(gòu),例如Queue或者Pipe或者其他,在使用pool的時(shí)候,可以直接得到結(jié)果,map和imap都是直接得到一個(gè)list和可迭代對象,而apply_async得到的結(jié)果需要用一個(gè)list裝起來,然后得到每個(gè)結(jié)果。
以上這篇深入理解python多進(jìn)程編程就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 淺析Python中的多進(jìn)程與多線程的使用
- Python中使用多進(jìn)程來實(shí)現(xiàn)并行處理的方法小結(jié)
- Python控制多進(jìn)程與多線程并發(fā)數(shù)總結(jié)
- Python多線程、異步+多進(jìn)程爬蟲實(shí)現(xiàn)代碼
- python 多進(jìn)程通信模塊的簡單實(shí)現(xiàn)
- 探究Python多進(jìn)程編程下線程之間變量的共享問題
- Python實(shí)現(xiàn)多進(jìn)程共享數(shù)據(jù)的方法分析
- 淺談Python 多進(jìn)程默認(rèn)不能共享全局變量的問題
- 在Python程序中實(shí)現(xiàn)分布式進(jìn)程的教程
- python3學(xué)習(xí)筆記之多進(jìn)程分布式小例子
- Python多進(jìn)程入門、分布式進(jìn)程數(shù)據(jù)共享實(shí)例詳解
相關(guān)文章
pandas創(chuàng)建DataFrame的7種方法小結(jié)
這篇文章主要介紹了pandas創(chuàng)建DataFrame的7種方法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Python實(shí)現(xiàn)抓取城市的PM2.5濃度和排名
本文給大家介紹的是一則使用Python實(shí)現(xiàn)抓取城市的PM2.5數(shù)據(jù)和排名,2015-03-03詳解Selenium-webdriver繞開反爬蟲機(jī)制的4種方法
這篇文章主要介紹了詳解Selenium-webdriver繞開反爬蟲機(jī)制的4種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Python開發(fā)之城堡保衛(wèi)戰(zhàn)游戲的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了如何利用Python編寫一個(gè)簡單的城堡保衛(wèi)戰(zhàn)游戲,本文用到了os文件讀寫,pygame模塊以及面向?qū)ο笏枷?,感興趣的可以了解一下2023-01-01python監(jiān)控進(jìn)程狀態(tài),記錄重啟時(shí)間及進(jìn)程號的實(shí)例
今天小編就為大家分享一篇python監(jiān)控進(jìn)程狀態(tài),記錄重啟時(shí)間及進(jìn)程號的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07詳解PyTorch手寫數(shù)字識別(MNIST數(shù)據(jù)集)
這篇文章主要介紹了詳解PyTorch手寫數(shù)字識別(MNIST數(shù)據(jù)集),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08matplotlib命令與格式之tick坐標(biāo)軸日期格式(設(shè)置日期主副刻度)
這篇文章主要介紹了matplotlib命令與格式之tick坐標(biāo)軸日期格式(設(shè)置日期主副刻度),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08python繪圖時(shí),坐標(biāo)軸負(fù)號顯示不出來的解決
這篇文章主要介紹了python繪圖時(shí),坐標(biāo)軸負(fù)號顯示不出來的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09