Python多進程(Multiprocessing)的簡單使用

Python由於GIL的存在,多線程(Thread)、協程(Asyncio)可以實現併發,並行則依賴多進程(Multiprocessing)實現。

多進程的學習可以參考廖雪峯Python教程和Python標準庫。

本文就Multiprocessing的日常使用做個demo。

一、通過Process創建多進程

實現多進程,可以創建多個Process對象,並調用start()去生成進程,通過join()等待完成。

from multiprocessing import Process
import os
import time
import random

def run(name: str):
    print(f"Child process {os.getpid()} do {name} task, parent process {os.getppid()}")
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print(f'Task {name} runs {end - start} seconds.')

def main():
    # test process
    start = time.time()
    print(f'Parent process {os.getpid()}')
    p1 = Process(target=run, args=('test1',))
    p2 = Process(target=run, args=('test2',))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('Child process end')
    end = time.time()
    print(f'All Tasks runs {end - start} seconds.')


if __name__ == '__main__':
    main()

效果如圖,parent進程62488,創建的子進程分別爲62489和62450,共用時0.38s,小於分別執行的0.3s和0.36s之和,說明並行。


二、通過Pool進程池創建

一般情況下,不會通過Process這種低級API去創建多進程,可以通過Pool進程池去創建。Pool(num),num指定工作進程數目,默認通過os.cpu_count()獲取CPU核數,通過apply_async註冊函數。

from multiprocessing import Pool
import subprocess
import os
import time
import random

def pool_run(name: str):
    print(f'Child process {os.getpid()} Run task {name}')
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print(f'Task {name} runs {end - start} seconds.')
    return name

def main():
    # test pool
    print(f'Parent process {os.getpid()}')
    p = Pool(4)
    result = []
    for i in range(5):
        result.append(p.apply_async(pool_run, args=(i,)))
    p.close()
    p.join()
    for res in result:
        print(res.get())
    print('All subprocesses done.')


if __name__ == '__main__':
    main()

效果如圖,總共5個任務,4個進程。parent進程62577,同時創建4個子進程去處理任務,在完成其中1個任務後,該空餘的進程繼續執行第5個任務。

三、進程間通信

Multiprocessing提供兩種標準的進程通信方式Quere、Pipe。在比如生產者-消費者這種模型可能會用到。

from multiprocessing import Process, Queue
import os
import time
import random

def write(q):
    print(f'Process to write: {os.getpid()}')
    for value in ['A', 'B', 'C']:
        print(f'Put {value} to queue...')
        q.put(value)
        time.sleep(random.random())


def read(q):
    print(f'Process to read: {os.getpid()}')
    while True:
        value = q.get(True)
        print(f'Get {value} from queue.')


def main():
    # test 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()


if __name__ == '__main__':
    main()

效果如圖,進程62773生產,進程62774消費,兩者並行,通過隊列queue做通信。

四、總結

可能一般情況下,用Pool即可。

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