多進程學習總結
Python 解釋器有一個全局解釋器鎖(PIL),導致每個 Python 進程中最多同時運行一個線程,因此 Python 多線程程序並不能改善程序性能,不能發揮多核系統的優勢,可以通過這篇文章了解。但是多進程程序不受此影響, Python 2.6 引入了 multiprocessing 來解決這個問題。這裏介紹 multiprocessing 模塊下的進程,進程同步,進程間通信和進程管理四個方面的內容。 這裏主要講解多進程的典型使用,multiprocessing 的 API 幾乎是完復制了 threading 的API, 因此只需花少量的時間就可以熟悉 threading 編程了。
一 起多進程
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()
三 利用os模塊來查看各個進程ID
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
四 進程間的數據傳遞---queue
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傳遞的值,在主進程中也可以取出來.
和多線程之間數據傳遞(共享比較)
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,因為線程之間數據是共享的,在多進程中,如果不傳遞,則在子進程中就會報未定義的錯誤,
不能把線程queue作為參數傳給子進程
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作為參數傳給子進程.
五 進程間的數據傳遞---Pipe
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 對象後就自動生成兩個返回對象,可以理解成兩頭,
也有兩個方法 send() 發送數據 recv()接收數據.
六 進程間的數據傳遞---manager
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一樣,只是是異步執行.
多進程學習總結