什麼是進程,什麼是線程?python
進程與線程是包含關係,進程包含了線程。app
進程是系統資源分配的最小單元,線程是系統任務執行的最小單元。dom
打個比方,打開word,word這個程序是一個進程,裏面的拼寫檢查,字數統計,更改字體等等功能是一個個線程。當word這個進程啓動的時候,系統分配給word進程一些資源(CPU,內存等),當某個線程執行時須要資源時,就從word進程的資源池裏取。async
關於Python的多進程實例,咱們能夠用Python的multiprocessing package來實現。函數
multiprocessing模塊提供了一個Process類來表明一個進程對象,下面的例子演示了啓動一個子進程並等待其結束:字體
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('Child process will start.') p.start() p.join() print('Child process end.')
執行結果:spa
Parent process 928. Process will start. Run child process test (929)... Process end.
建立子進程時,只須要傳入一個執行函數和函數的參數,建立一個Process實例,用start()方法啓動,這樣建立進程比fork()還要簡單。join()方法能夠等待子進程結束後再繼續往下運行,一般用於進程間的同步。操作系統
若是要啓動大量的子進程,能夠用進程池的方式批量建立子進程:.net
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(4) 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 669. Waiting for all subprocesses done... Run task 0 (671)... Run task 1 (672)... Run task 2 (673)... Run task 3 (674)... Task 2 runs 0.14 seconds. Run task 4 (673)... Task 1 runs 0.27 seconds. Task 3 runs 0.86 seconds. Task 0 runs 1.41 seconds. Task 4 runs 1.91 seconds. All subprocesses done.
代碼解讀: 對Pool對象調用join()方法會等待全部子進程執行完畢,調用join()以前必須先調用close(),調用close()以後就不能繼續添加新的Process了。
請注意輸出的結果,task 0,1,2,3是馬上執行的,而task 4要等待前面某個task完成後才執行,這是由於Pool的默認大小在個人電腦上是4,所以,最多同時執行4個進程。這是Pool有意設計的限制,並非操做系統的限制。若是改爲p=Pool(5),就能夠跑5個進程
不少時候,子進程並非自身,而是一個外部進程。咱們建立了子進程後,還須要控制子進程的輸入和輸出。
subprocess
模塊可讓咱們很是方便地啓動一個子進程,而後控制其輸入和輸出。
下面的例子演示瞭如何在Python代碼中運行命令nslookup www.python.org
,這和命令行直接運行的效果是同樣的:
import subprocess print('$ nslookup www.python.org') r = subprocess.call(['nslookup', 'www.python.org']) print('Exit code:', r)
運行結果:
$ nslookup www.python.org Server: 192.168.19.4 Address: 192.168.19.4#53 Non-authoritative answer: www.python.org canonical name = python.map.fastly.net. Name: python.map.fastly.net Address: 199.27.79.223 Exit code: 0
若是子進程還須要輸入,則能夠經過communicate()
方法輸入:
import subprocess print('$ nslookup') p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = p.communicate(b'set q=mx\npython.org\nexit\n') print(output.decode('utf-8')) print('Exit code:', p.returncode)
上面的代碼至關於在命令行執行命令nslookup
,而後手動輸入:
set q=mx
python.org
exit
運行結果以下:
$ nslookup Server: 192.168.19.4 Address: 192.168.19.4#53 Non-authoritative answer: python.org mail exchanger = 50 mail.python.org. Authoritative answers can be found from: mail.python.org internet address = 82.94.164.166 mail.python.org has AAAA address 2001:888:2000:d::a6 Exit code: 0
Process
之間確定是須要通訊的,操做系統提供了不少機制來實現進程間的通訊。Python的multiprocessing
模塊包裝了底層的機制,提供了Queue
、Pipes
等多種方式來交換數據。
咱們以Queue
爲例,在父進程中建立兩個子進程,一個往Queue
裏寫數據,一個從Queue
裏讀數據:
from multiprocessing import Process, Queue import os, time, random # 寫數據進程執行的代碼: def write(q): print('Process to write: %s' % os.getpid()) for value in ['A', 'B', 'C']: print('Put %s to queue...' % value) q.put(value) time.sleep(random.random()) # 讀數據進程執行的代碼: def read(q): print('Process to read: %s' % os.getpid()) 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()
運行結果以下:
Process to write: 50563 Put A to queue... Process to read: 50564 Get A from queue. Put B to queue... Get B from queue. Put C to queue... Get C from queue.
在Unix/Linux下,multiprocessing
模塊封裝了fork()
調用,使咱們不須要關注fork()
的細節。因爲Windows沒有fork
調用,所以,multiprocessing
須要「模擬」出fork
的效果,父進程全部Python對象都必須經過pickle序列化再傳到子進程去,全部,若是multiprocessing
在Windows下調用失敗了,要先考慮是否是pickle失敗了。