Python标准库10 多进程初步 (multiprocessing包)
作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉(zhuǎn)載,也請(qǐng)保留這段聲明。謝謝!
?
我們已經(jīng)見過了使用subprocess包來創(chuàng)建子進(jìn)程,但這個(gè)包有兩個(gè)很大的局限性:1) 我們總是讓subprocess運(yùn)行外部的程序,而不是運(yùn)行一個(gè)Python腳本內(nèi)部編寫的函數(shù)。2) 進(jìn)程間只通過管道進(jìn)行文本交流。以上限制了我們將subprocess包應(yīng)用到更廣泛的多進(jìn)程任務(wù)。(這樣的比較實(shí)際是不公平的,因?yàn)閟ubprocessing本身就是設(shè)計(jì)成為一個(gè)shell,而不是一個(gè)多進(jìn)程管理包)
?
threading和multiprocessing
(請(qǐng)盡量先閱讀Python多線程與同步)
multiprocessing包是Python中的多進(jìn)程管理包。與threading.Thread類似,它可以利用multiprocessing.Process對(duì)象來創(chuàng)建一個(gè)進(jìn)程。該進(jìn)程可以運(yùn)行在Python程序內(nèi)部編寫的函數(shù)。該P(yáng)rocess對(duì)象與Thread對(duì)象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition類 (這些對(duì)象可以像多線程那樣,通過參數(shù)傳遞給各個(gè)進(jìn)程),用以同步進(jìn)程,其用法與threading包中的同名類一致。所以,multiprocessing的很大一部份與threading使用同一套API,只不過換到了多進(jìn)程的情境。
但在使用這些共享API的時(shí)候,我們要注意以下幾點(diǎn):
- 在UNIX平臺(tái)上,當(dāng)某個(gè)進(jìn)程終結(jié)之后,該進(jìn)程需要被其父進(jìn)程調(diào)用wait,否則進(jìn)程成為僵尸進(jìn)程(Zombie)。所以,有必要對(duì)每個(gè)Process對(duì)象調(diào)用join()方法 (實(shí)際上等同于wait)。對(duì)于多線程來說,由于只有一個(gè)進(jìn)程,所以不存在此必要性。
- multiprocessing提供了threading包中沒有的IPC(比如Pipe和Queue),效率上更高。應(yīng)優(yōu)先考慮Pipe和Queue,避免使用Lock/Event/Semaphore/Condition等同步方式 (因?yàn)樗鼈冋紦?jù)的不是用戶進(jìn)程的資源)。
- 多進(jìn)程應(yīng)該避免共享資源。在多線程中,我們可以比較容易地共享資源,比如使用全局變量或者傳遞參數(shù)。在多進(jìn)程情況下,由于每個(gè)進(jìn)程有自己獨(dú)立的內(nèi)存空間,以上方法并不合適。此時(shí)我們可以通過共享內(nèi)存和Manager的方法來共享資源。但這樣做提高了程序的復(fù)雜度,并因?yàn)橥降男枰档土顺绦虻男省?span style="color:#000000;">
Process.PID中保存有PID,如果進(jìn)程還沒有start(),則PID為None。
?
我們可以從下面的程序中看到Thread對(duì)象和Process對(duì)象在使用上的相似性與結(jié)果上的不同。各個(gè)線程和進(jìn)程都做一件事:打印PID。但問題是,所有的任務(wù)在打印的時(shí)候都會(huì)向同一個(gè)標(biāo)準(zhǔn)輸出(stdout)輸出。這樣輸出的字符會(huì)混合在一起,無法閱讀。使用Lock同步,在一個(gè)任務(wù)輸出完成之后,再允許另一個(gè)任務(wù)輸出,可以避免多個(gè)任務(wù)同時(shí)向終端輸出。
# Similarity and difference of multi thread vs. multi process # Written by Vameiimport os import threading import multiprocessing# worker function def worker(sign, lock):lock.acquire()print(sign, os.getpid())lock.release()# Main print('Main:',os.getpid())# Multi-thread record = [] lock = threading.Lock() for i in range(5):thread = threading.Thread(target=worker,args=('thread',lock))thread.start()record.append(thread)for thread in record:thread.join()# Multi-process record = [] lock = multiprocessing.Lock() for i in range(5):process = multiprocessing.Process(target=worker,args=('process',lock))process.start()record.append(process)for process in record:process.join()所有Thread的PID都與主程序相同,而每個(gè)Process都有一個(gè)不同的PID。
(練習(xí): 使用mutiprocessing包將Python多線程與同步中的多線程程序更改為多進(jìn)程程序)
?
Pipe和Queue
正如我們?cè)贚inux多線程中介紹的管道PIPE和消息隊(duì)列message queue,multiprocessing包中有Pipe類和Queue類來分別支持這兩種IPC機(jī)制。Pipe和Queue可以用來傳送常見的對(duì)象。
?
1) Pipe可以是單向(half-duplex),也可以是雙向(duplex)。我們通過mutiprocessing.Pipe(duplex=False)創(chuàng)建單向管道 (默認(rèn)為雙向)。一個(gè)進(jìn)程從PIPE一端輸入對(duì)象,然后被PIPE另一端的進(jìn)程接收,單向管道只允許管道一端的進(jìn)程輸入,而雙向管道則允許從兩端輸入。
下面的程序展示了Pipe的使用:
?
# Multiprocessing with Pipe # Written by Vameiimport multiprocessing as muldef proc1(pipe):pipe.send('hello')print('proc1 rec:',pipe.recv())def proc2(pipe):print('proc2 rec:',pipe.recv())pipe.send('hello, too')# Build a pipe pipe = mul.Pipe()# Pass an end of the pipe to process 1 p1 = mul.Process(target=proc1, args=(pipe[0],)) # Pass the other end of the pipe to process 2 p2 = mul.Process(target=proc2, args=(pipe[1],)) p1.start() p2.start() p1.join() p2.join()這里的Pipe是雙向的。
Pipe對(duì)象建立的時(shí)候,返回一個(gè)含有兩個(gè)元素的表,每個(gè)元素代表Pipe的一端(Connection對(duì)象)。我們對(duì)Pipe的某一端調(diào)用send()方法來傳送對(duì)象,在另一端使用recv()來接收。
?
2) Queue與Pipe相類似,都是先進(jìn)先出的結(jié)構(gòu)。但Queue允許多個(gè)進(jìn)程放入,多個(gè)進(jìn)程從隊(duì)列取出對(duì)象。Queue使用mutiprocessing.Queue(maxsize)創(chuàng)建,maxsize表示隊(duì)列中可以存放對(duì)象的最大數(shù)量。
下面的程序展示了Queue的使用:
# Written by Vamei import os import multiprocessing import time #================== # input worker def inputQ(queue):info = str(os.getpid()) + '(put):' + str(time.time())queue.put(info)# output worker def outputQ(queue,lock):info = queue.get()lock.acquire()print (str(os.getpid()) + '(get):' + info)lock.release() #=================== # Main record1 = [] # store input processes record2 = [] # store output processes lock = multiprocessing.Lock() # To prevent messy print queue = multiprocessing.Queue(3)# input processes for i in range(10):process = multiprocessing.Process(target=inputQ,args=(queue,))process.start()record1.append(process)# output processes for i in range(10):process = multiprocessing.Process(target=outputQ,args=(queue,lock))process.start()record2.append(process)for p in record1:p.join()queue.close() # No more object will come, close the queuefor p in record2:p.join()?一些進(jìn)程使用put()在Queue中放入字符串,這個(gè)字符串中包含PID和時(shí)間。另一些進(jìn)程從Queue中取出,并打印自己的PID以及get()的字符串。
?
總結(jié)
Process, Lock, Event, Semaphore, Condition
Pipe, Queue
轉(zhuǎn)載于:https://www.cnblogs.com/vamei/archive/2012/10/12/2721484.html
總結(jié)
以上是生活随笔為你收集整理的Python标准库10 多进程初步 (multiprocessing包)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 科帕奇中控中间最长的装饰条叫什么?
- 下一篇: C#各种加密算法的研究