Python - 多進程使用教程

什麼是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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章