什麼是Multiprocessing?
大部分計算機cpu都是多核的,爲了提高效率,把程序分配到多個核裏面同時運行,這就叫多進程。
Python提供了一個mulitprocessing 庫來實現多進程
(本文是學習“莫煩Python後寫的總結,分析與感悟。這裏是莫煩python的連接: >點這裏< )
1:基本操作:創建進程
a. 創建一個函數,且不能有返回值
b. 創建子進程對象,函數名傳遞給 target,參數放在一個可迭代對象內傳遞給 args,(注意,若只有一個元素1,且是放在小括號內,則應該寫爲:args = (1,) 因爲加個逗號纔算是元組,纔可迭代)
c. 運行子進程(注意!!需要在if __name__ == '__main__':下運行)
'''創建進程'''
import multiprocessing as mp
def job_1(a, b):
for _ in range(5):
print("job_1 is running")
if __name__ == '__main__':
print("main start")
#創建子進程
p1 = mp.Process(target=job_1, args=(1, 2))
#子進程開始運行
p1.start()
print("main end")
這個時候,main相當於主進程,p1是他的子進程,一旦運行到p1.start(),job1纔開始運行。而main 和 p1各自運行各自的,並不會相互等待,所以就出現了以下結果:
2.等待主進程:join()
join()的官方解釋:阻塞當前進程,直到調用join方法的那個進程執行完。
意思就是:等當前子進程(p1 或 p2) 運行完畢之後,主進程(main)才繼續運行。(注意:如果兩個子進程都使用join,子進程之間不會互相等待,仍然是各走各的)
# 只需要把join放在主進程需要等待的地方即可
準備工作:
import multiprocessing as mp
import time
def run_1():
for _ in range(5):
time.sleep(0.1)
print("job_1 is runing...")
def run_2():
for _ in range(5):
time.sleep(0.1)
print("job_2 is runing...")
if __name__ == '__main__':
print("main start")
p1 = mp.Process(target=run_1)
p2 = mp.Process(target=run_2)
情況1:main, p1, p2 三個進程互不干擾
p1.start()
p2.start()
print("main end")
結果:各自運行,非常混亂
情況2:p1, p2都調用join()
p1.start()
p2.start()
p1.join()
p2.join()
print("main end")
結果:主程序等待p1, p2運行完了之後才運行,但是p1, p2依舊混亂
情況3:p1調用join()之後再調用p2
p1.start()
p1.join()
p2.start()
print("main end")
結果有些許奇怪,這是爲什麼呢?因爲p1 調用join之後,main程序在等待p1運行完的時候,根本還沒開始運行代碼 p2.start() !
"
3.特殊的返回值的方法:Queue
多進程中,可以利用隊列來存儲返回值,在進程結束之後再調取存儲的返回值
a.聲明一個queue,並把queue作爲參數傳入方法中
b.待進程結束後提取 “返回值”
import multiprocessing as mp
import time
def job_1(q, a, b):
sum = a * b
q.put(sum)
def job_2(q, a, b):
sum = a / b
q.put(sum)
if __name__ == '__main__':
q = mp.Queue()
p1 = mp.Process(target=job_1, args=(q, 1, 2))
p2 = mp.Process(target=job_2, args=(q, 1, 2))
p1.start()
p2.start()
p1.join()
p2.join()
print(q.get(), q.get())
4.一個池子:pool
pool也叫做進程池,就是我們將所要運行的東西,放到池子裏,Python會自行解決多進程的問題
用法0:自定義核的數量:pool = mp.Pool(processes = 2) 即pool對象運行時只使用兩個核
用法1:給pool對象一個函數,並傳入一個迭代對象(迭代的是傳入的參數),pool會返回一個返回值列表
import multiprocessing as mp
def job(a):
return a*a
if __name__ == '__main__':
pool = mp.Pool(processes = 2)
returns = pool.map(job, range(10))
print(returns)
用法2:apply_async()方法,這個方法類似於直接調用start(),但它接受了一個返回值,而且一個方法只使用一個核。
if __name__ == '__main__':
pool = mp.Pool()
returns = pool.apply_async(job, (3,))
print(returns.get())
5.共同處理數據:共享內存
如果不用共享變量,傳入子程序的參數只是一個副本,並不能改變傳入的變量的值,也不能相互交流
所以在多核處理的特殊情況下,我們需要用到共享內存處理、需要子進程之間相互交流數據的情況
操作1:定義單個共享變量:v = mp.Value('i', 0)
這裏我們呢在兩個子程序裏面分別對變量加 5 次 2,和 5 次 4 預想的最終結果是 30
import multiprocessing as mp
def run_1(a):
for _ in range(5):
a.value = a.value + 2
print(a.value)
def run_2(a):
for _ in range(5):
a.value = a.value + 4
print(a.value)
if __name__ == '__main__':
v = mp.Value('i', 0)
p1 = mp.Process(target=run_1, args=(v,))
p2 = mp.Process(target=run_2, args=(v,))
p1.start()
p2.start()
p1.join()
p2.join()
print(v.value)
輸出:發現數據實現了共享,且最後也確實被改變成了30。
2
8
10
12
14
6
18
22
26
30
30
操作2:定義變量數組:(注意!!這裏只能定義一維數組)
array = mp.Array('i', [1, 2, 3, 4])
6.維持秩序:lock
這裏回去第 5 節的輸出結果,會發現共享的變量被兩個進程搶着用,一會兒加2, 一會兒加4, 一會兒你print,一會兒我print
有時候我們需要兩個程序有順序的完成各自的任務,不能讓他們搶奪變量,這是後就可以用到 進程鎖
import multiprocessing as mp
def run_1(a, L):
#鎖住
L.acquire()
for _ in range(5):
a.value = a.value + 2
print(a.value)
L.release()
#釋放
def run_2(a, L):
L.acquire()
for _ in range(5):
a.value = a.value + 4
print(a.value)
L.release()
if __name__ == '__main__':
v = mp.Value('i', 0)
#創建進程索
L = mp.Lock()
p1 = mp.Process(target=run_1, args=(v,L))
p2 = mp.Process(target=run_2, args=(v,L))
p1.start()
p2.start()
p1.join()
p2.join()
print(v.value)
這樣一來,輸出就變得有序起來了:
2
4
6
8
10
14
18
22
26
30
30