進程
說明:本文是基於Py2.X環境,
Python實現多進程的方式主要有兩種:一種方法是使用os模塊中的fork方法; 另一種是使用multiprocessing模塊。這兩種方法的區別在於前者僅適用於Unix/Linux操作操作。對win是不支持的,而後者則是跨平臺的實現方式。
使用os模塊中的fork方式實現多進程。
Unix/Linux操作系統提供了一個fork()系統調用,它非常特殊。普通的函數調用,調用一次,返回一次,但是fork()調用一次,返回兩次,因爲操作系統自動把當前進程(稱爲父進程)複製了一份(稱爲子進程),然後,分別在父進程和子進程內返回。
子進程永遠返回0,而父進程返回子進程的ID。這樣做的理由是,一個父進程可以fork出很多子進程,所以,父進程要記下每個子進程的ID,而子進程只需要調用getppid()就可以拿到父進程的ID。
Python的os模塊封裝了常見的系統調用,其中就包括fork,可以在Python程序中輕鬆創建子進程:
import os print 'Process (%s) start...' % os.getpid() pid = os.fork() if pid == 0: print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()) else: print 'I (%s) just created a child process (%s).' % (os.getpid(), pid) 得到: Process (2450) start... I (2450) just created a child process (2451). I am child process (2451) and my parent is 2450.
使用Multiprocessing查模塊創建多進程。
multiprocessing模塊提供了一個Process類來描述一個進程對象,創建子進程時,只需要傳入一個執行函數和函數的參數即可完成一個Process實例的創建,用start()方法啓動進程,用join()方法實現進程間的同步。join()方法可以等待子進程結束後再繼續往下運行,通常用於進程間的同步。
# -*- coding:utf-8 -*- from multiprocessing import Process import os # 子進程要執行的代碼 def run_proc(name): print 'Run child process %s (%s)...' % (name, os.getpid()) if __name__ == '__main__': print 'Parent process %s.' % os.getpid() p = Process(target=run_proc, args=('test',)) print 'Process will start.' p.start() p.join() print 'Process end.' 得到: Parent process 2533. Process will start. Run child process test (2534)... Process end.
multiprocessing模塊提供了一個pool類來代表進程池對象
Pool可以提供指定數量的進程供用戶調用,默認大小是cpu的核數,當有新的請求提交到pool中時,如果池還沒有滿,那麼就會創建一個新的進程用來執行該請求,但如果池的進程數已經達到規定最大值,那麼該請求就會等待,直到池中有進程結束纔會創建新的進程來處理它。
# -*- coding:utf-8 -*- from multiprocessing import Pool import os, time, random def long_time_task(name): print 'Run task %s (%s)...' % (name, os.getpid()) start = time.time() time.sleep(random.random() * 3) end = time.time() print 'Task %s runs %0.2f seconds.' % (name, (end - start)) if __name__ == '__main__': print 'Parent process %s.' % os.getpid() p = Pool() for i in range(5): p.apply_async(long_time_task, args=(i,)) print 'Waiting for all subprocesses done...' p.close() p.join() print 'All subprocesses done.' 得到: Parent process 2541. Waiting for all subprocesses done... Run task 0 (2543)... Run task 1 (2544)... Run task 2 (2545)... Run task 3 (2546)... Task 0 runs 0.02 seconds. Run task 4 (2543)... Task 2 runs 0.60 seconds. Task 4 runs 1.18 seconds. Task 3 runs 1.26 seconds. Task 1 runs 1.66 seconds. All subprocesses done.
對Pool對象調用join()方法會等待所有子進程執行完畢,調用join()之前必須先調用close(),調用close()之後就不能繼續添加新的Process了。
進程間的通信
Process之間肯定是需要通信的,操作系統提供了很多機制來實現進程間的通信。Python的multiprocessing模塊包裝了底層的機制,提供了Queue、Pipes等多種方式來交換數據。兩者的區別在於Pipe常用於兩個進程間的通訊而Queue用於多個進程間實現通訊。
Queue通訊
Queue是多進程安全的隊列,可以使用Queue實現多進程之間的數據傳輸,有兩個方法:put和get進行Queue操作。
- put方法用以插入數據隊列中它可以有兩個可選參數:blocked和timeout,如果blocked爲True(默認值)並且timeout是正值,該方法會阻塞timeout指定的時間,直到該隊列有剩餘空間,如果超時,會拋出Queue.Full異常,如果blocked爲False,但該Queue已滿,則會立即拋出Queue.Full異常。
- Get方法用以從隊列讀取並且刪除一個元素。它可以有兩個可選參數:blocked和timeout,如果blocked爲True(默認值)並且timeout是正值,那麼在等待時間內沒有取到任何元素會拋出Queue.Empty異常,如果blocked爲False,分兩種情況:如果Queue有一個值木口月禾,則立即返回該值,否則如果隊列爲空,則立即拋出Queuq.Empty異常。
# -*- coding:utf-8 -*- from multiprocessing import Process, Queue import os, time, random # 寫數據進程執行的代碼: def write(q): for value in ['A', 'B', 'C']: print 'Put %s to queue...' % value q.put(value) time.sleep(random.random()) # 讀數據進程執行的代碼: def read(q): while True: value = q.get(True) print 'Get %s from queue.' % value if __name__ == '__main__': # 父進程創建Queue,並傳給各個子進程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 啓動子進程pw,寫入: pw.start() # 啓動子進程pr,讀取: pr.start() # 等待pw結束: pw.join() # pr進程裏是死循環,無法等待其結束,只能強行終止: pr.terminate() 得到: Put A to queue... Get A from queue. Put B to queue... Get B from queue. Put C to queue... Get C from queue.
Pipes通訊
Pipe常用來在兩個進程間進行通信,兩個進程分別位於管道的兩端。
Pipe方法返回(conn1,conn2)代表一個管道的兩個端,Pipe方法有duplex參數,如果duplex參數爲True(默認值),那麼這個管道是全雙工模式,也就是說conn1和conn2均可收發,若duplex爲False,conn1只負責接收消息,conn2只負責發送消息。send和recv方法分別是發送和接收消息的方法。例如,在全雙工模式下,可以調用conn1.send發送消息,conn1.recv接收消息。如果沒有消息可接收,recv方法會一直阻塞。如果管道已經被關閉,那麼recv方法會拋出EOFError.
import multiprocessing import random import time, os def proc_send(pipe, urls): for url in urls: print "process(%s) send:%s" % (os.getpid(), url) pipe.send(url) time.sleep(random.random()) def proc_recv(pipe): while True: print "Process(%s) rev:%s" % (os.getpid(), pipe.recv()) time.sleep(random.random()) if __name__ == "__main__": pipe = multiprocessing.Pipe() p1 = multiprocessing.Process(target=proc_send,args=(pipe[0],['url_'+str(i) for i in range(10)])) p2 = multiprocessing.Process(target=proc_recv,args=(pipe[1],)) p1.start() p2.start() p1.join() p2.join() 得到: process(1134) send:url_0 Process(1135) rev:url_0 process(1134) send:url_1 Process(1135) rev:url_1 process(1134) send:url_2 Process(1135) rev:url_2 process(1134) send:url_3 Process(1135) rev:url_3 process(1134) send:url_4 Process(1135) rev:url_4 process(1134) send:url_5 Process(1135) rev:url_5 process(1134) send:url_6 Process(1135) rev:url_6 process(1134) send:url_7 Process(1135) rev:url_7 process(1134) send:url_8 Process(1135) rev:url_8 process(1134) send:url_9 Process(1135) rev:url_9