Python 解釋器有一個全局解釋器鎖(PIL),致使每一個 Python 進程中最多同時運行一個線程,所以 Python 多線程程序並不能改善程序性能,不能發揮多核系統的優點,能夠經過這篇文章瞭解。可是多進程程序不受此影響, Python 2.6 引入了 multiprocessing 來解決這個問題。這裏介紹 multiprocessing 模塊下的進程,進程同步,進程間通訊和進程管理四個方面的內容。 這裏主要講解多進程的典型使用,multiprocessing 的 API 幾乎是完複製了 threading 的API, 所以只需花少許的時間就能夠熟悉 threading 編程了。python
from multiprocessing import Process, current_process import time def func(i): time.sleep(2) proc = current_process() print("proce.name : ", proc.name) # 輸出進程名 print("proc.pid :", proc.pid) # 輸出進程ID if __name__ == "__main__": for i in range(10): p = Process(target=func, args=(i,)) p.start() # p.join() # p.join() 加上這一句以後,就是要等這個進程執行完以後才能進入下一個循環,也就是才能執行下一個進程,這樣就失去了多進程的意義
from multiprocessing import Process, current_process import time import threading def foo(): print("threading id is ", threading.get_ident()) # 獲取當前線和的ID def func(i): time.sleep(2) proc = current_process() print("proce.name : ", proc.name) # 輸出進程名 print("proc.pid :", proc.pid) # 輸出進程ID p = threading.Thread(target=foo, ) # 進程中再起線程 p.start() if __name__ == "__main__": for i in range(10): p = Process(target=func, args=(i,)) p.start()
from multiprocessing import Process import os def info(title): print(title) print('module name:', __name__) print('parent process:', os.getppid()) #查看父進程ID print('process id:', os.getpid()) #查看當前進程ID print("\n\n") def f(name): info('called from child process function f ') print('hello', name) if __name__ == '__main__': info('main process line') p = Process(target=f, args=('bob',)) p.start() # p.join()
結果以下: 能夠看到,主進程的id就是子進程的父ID編程
from multiprocessing import Process, Queue import threading def f(q): q.put([42, None, 'hello']) # 子進程中put 一個值進入Queue if __name__ == '__main__': q = Queue() q.put("test123") # 主進程中put 一個值進入Queue p = Process(target=f, args=(q,)) p.start() p.join() print("444", q.get_nowait()) print("444", q.get_nowait())
能夠看到,在這個Queue中取出兩個值,因此在子進程中給這個Queue傳遞的值,在主進程中也能夠取出來.windows
import threading import queue def f(): q.put([42, None, 'hello']) if __name__ == '__main__': q = queue.Queue() q.put("test123") p = threading.Thread(target=f, ) p.start() print("444", q.get_nowait()) print("444", q.get_nowait())
結果是:多線程
和進程數據傳遞比較:這裏不用傳遞這個queue,由於線程之間數據是共享的,在多進程中,若是不傳遞,則在子進程中就會報未定義的錯誤,app
from multiprocessing import Process, Queue import threading import queue def f(q): q.put([42, None, 'hello']) if __name__ == '__main__': q = queue.Queue() q.put("test123") p = Process(target=f,args=(q,)) p.start() print("444", q.get_nowait()) print("444", q.get_nowait())
要想在進程之間傳遞數據,只能是進程queue,不能把線程queue做爲參數傳給子進程.異步
from multiprocessing import Process, Pipe def f(conn): conn.send([42, None, 'hello from child']) print("",conn.recv()) # prints "from main" conn.close() if __name__ == '__main__': parent_conn, child_conn = Pipe() #產生兩個返回對象,分別表明兩頭 p = Process(target=f, args=(child_conn,)) p.start() print("parent",parent_conn.recv()) # prints "[42, None, 'hello from child']" parent_conn.send("from main") p.join()
生成一個Pipe 對象後就自動生成兩個返回對象,能夠理解成兩頭,async
也有兩個方法 send() 發送數據 recv()接收數據.ide
from multiprocessing import Process, Manager import os def f(dct, lst): d[os.getpid()] = os.getppid() l.append(os.getpid()) # print(l) if __name__ == '__main__': with Manager() as manager: dct = manager.dict() # {} #生成一個manager的字典(不是日常的字典),可在多個進程間共享和傳遞. lst = manager.list(range(5)) # 生成一個列表,可在多個進程間共享和傳遞 p_list = [] for i in range(10): p = Process(target=f, args=(dct, lst)) p.start() p_list.append(p) for res in p_list: # 等待全部進程執行完閉結果,這裏如如不寫就會報錯,說系統找不到指定文件 res.join() print(dct) print(lst)
結果以下:函數
from multiprocessing import Process, Lock def f(l, i): l.acquire() #加鎖 print('hello world', i) l.release() #解鎖 if __name__ == '__main__': lock = Lock() for num in range(10): p=Process(target=f, args=(lock, num)) p.start() # p.join() # 加上這個以後就要當前進程執行完以後才執行下一下進程
結果以下:性能
from multiprocessing import Process, Lock def f(l, i): l.acquire() #加鎖 print('hello world', i) l.release() #解鎖 if __name__ == '__main__': lock = Lock() for num in range(10): p=Process(target=f, args=(lock, num)) p.start() p.join() # 加上這個以後就要當前進程執行完以後才執行下一下進程
結果以下
能夠看到,打印的循序是固定的,並且在執行行明顯知道,他是上一個進程執行完以後才執行的下一個進程.
按道理說,各進程之間數據是獨立的,各進程之個對同一分內存數據是不共享的,(上面寫的那幾種數據共享都不是對同一分內存數據共享,只是複製了以後再共享的),不該該加鎖啊,但這裏各個進程是共享屏幕的,加鎖的目的主要是爲了防止屏幕輸入輸出數據出錯.在python3中,進程鎖的意義不大.
from multiprocessing import Process, Pool, freeze_support import time import os def Foo(i): time.sleep(2) print("in process", os.getpid()) return i + 100 def Bar(arg): print('-->exec done:', arg, os.getpid()) if __name__ == '__main__': # 在windows 中必須寫在這個裏面,否則出錯 # freeze_support() pool = Pool(processes=5) # 容許進程池同時放入5個進程 print("主進程", os.getpid()) for i in range(10): pool.apply_async(func=Foo, args=(i,), callback=Bar) # callback=回調函數,是指這個進程執行完以後要執行的函數 # callback 函數是主進程調用的 # pool.apply(func=Foo, args=(i,)) #串行 # pool.apply_async(func=Foo, args=(i,)) #串行 print('end') pool.close() # 先close() 再join(), pool.join() # 進程池中進程執行完畢後再關閉,若是註釋的話,當主程度執行完以後程序就關閉,而不會等各子進程執行完 # 進程池的另外一種用法,這種方法不能調用callback函數 groups = [x * 20 for x in range(10)] pool = Pool(processes=5) pool.map(Foo, groups)
apply()是串行,也就是說只有等一個進程執行完才執行下一個進程,apply_async() 是並行,會按進程池中的個數一塊兒執行,進程池的方法不少,用些方法不經常使用. map_async() 這是一個異常的map方法,能夠調用callback()函數,用法和map同樣,只是是異步執行.