1. 程式人生 > 其它 >Python 多程序與程序池

Python 多程序與程序池

一、程序:一個程式執行起來後,程式碼+用到的資源稱之為程序,它是作業系統分配資源的基本單元。其也會有併發與並行的狀態,與多執行緒不同的是,多程序會充分利用CPU的資源來執行任務。

程序的狀態:
當任務數大於CPU的核數時,部分任務在執行,部分任務在等待執行,因此就會產生不通的狀態:
1.就緒狀態:執行條件以滿足,等待CPU執行。
2.執行狀態:CPU正在執行其任務。
3.等待狀態:等待某些條件滿足,例如一個程式sleep了。

multiprocessing模組
python中的多執行緒其實並不是真正的多執行緒,如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多程序。Python提供了非常好用的多程序包multiprocessing,其支援子程序、通訊和共享資料、執行不同形式的同步,提供了Process、Queue、Pipe、Lock等元件

Process:
使用方法與執行緒Thread類一樣如下,需要注意的是程序的建立與啟動必須放在__main__下面,否則會造成迴圈載入任務程式碼的問題。

from multiprocessing import Process

a = 0

def work(name):
    global a
    for i in range(100):
        a += 1
    print(f"{name}----", a)


def work1(name):
    global a
    for i in range(100):
        a += 1
    print(f"{name}
----"
, a) if __name__ == '__main__': p = Process(target=work, args=('MING',)) # 建立程序物件,通過args傳入任務引數 p1 = Process(target=work1,args=('CHAZ',),daemon=True) #設定程序是否為守護程序,如果是守護程序,則與主程序一起關閉。 p.start() # 啟動程序 p1.start() p.join() # 等待程序執行完成 p1.join() print('a:', a) >>>
MING---- 100 >>>CHAZ---- 100 >>>a: 0
 

os.getpid()檢視程序id,os.getppid(),檢視父程序id

import time,os
from multiprocessing import Process


def fun():
    time.sleep(2)
    print(f"fun--程序id{os.getpid()},父程序id{os.getppid()}")


def fun1():
    time.sleep(2)
    print(f"fun1--程序id{os.getpid()},父程序id{os.getppid()}")


if __name__ == '__main__':
    p = Process(target=fun)
    p1 = Process(target=fun1)
    p.start()
    p1.start()
    p.join()
    p1.join()
    print(f'主程序--{os.getpid()}')
 
>>> fun--程序id18188,父程序id14604
>>> fun1--程序id19256,父程序id14604
>>> 主程序--14604
 

從例1中可以看到程序之間的資訊是獨立的,並沒有共享,如果想實現程序之間的通訊可以使用Queue模組,(需要注意的是並非queue模組中的Queue,此佇列只能用來在一個程序中的多個執行緒之間使用)。而程序中的Queue可以在多個程序之間跨程序傳輸資料。
匯入:

from multiprocessing import Queue
 
  • 1

舉例:一個列表中有10個url地址,每個地址請求一次,使用2個程序程去傳送這 10個請求(假設請求每個地址需要0.5秒)
在不使用Queue的情況下:

from multiprocessing import Process
import time

li = ['www.baidu.com-{}'.format(i) for i in range(10)]


def work():
    while li:
        url = li.pop()
        print("請求地址:", url)
        time.sleep(0.5)


if __name__ == '__main__':
    t = Process(target=work)
    t1 = Process(target=work)
    t.start()
    t1.start()
    t.join()
    t1.join()
    
執行結果:
請求地址: www.baidu.com-9
請求地址: www.baidu.com-9
請求地址: www.baidu.com-8
請求地址: www.baidu.com-8
請求地址: www.baidu.com-7
請求地址: www.baidu.com-7
請求地址: www.baidu.com-6
.....
 

此時每個程序拿到的列表是獨立的,並不是共享的才造成上面的問題。如下,使用程序中的佇列解決這一問題:

from multiprocessing import Process, Queue
import time

def work(urls):
    while not urls.empty(): # 判斷佇列是否為空
        url = urls.get() # 獲取佇列中的值
        print("請求地址:", url)
        time.sleep(0.5)


if __name__ == '__main__':
    urls = Queue() # 建立佇列
    for i in range(10):
        urls.put(f"www.baidu.com-{i}") # 佇列中新增資料
    t = Process(target=work, args=(urls,)) # 建立程序物件,將佇列作為函式的引數
    t1 = Process(target=work, args=(urls,))
    t.start()
    t1.start()
    t.join()
    t1.join()
執行結果:

請求地址: www.baidu.com-0
請求地址: www.baidu.com-1
請求地址: www.baidu.com-2
請求地址: www.baidu.com-3
請求地址: www.baidu.com-4
請求地址: www.baidu.com-5
....
 

二、程序池
當需要建立的子程序數量不多時,可以直接使用Process動態生成多個程序,但如果是上百個甚至更多的目標,通過手動建立程序的工作量巨大,此時可以用到multiprocessing模組提供的Pool方法。

建立程序池時候可以指定最大程序數,當有新的請求要提交到Pool中時,會判斷Pool中有沒有達到最大程序數,如果達到,該請求就會等待,直到池中有程序結束,才會用之前的程序來執行新的任務。
程序池之間的通訊使用Manager中的Queue()。

from multiprocessing import Pool
import time

def func(msg):
    print("msg:", msg)
    time.sleep(3)

if __name__ == "__main__":
    po = Pool(3) # 建立程序池
    for i in range(4):
        msg = f"hello{i}"
        po.apply_async(func, (msg,))  # 維持執行的程序總數為processes,當一個程序執行完畢後會新增新的程序進去

    print("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
    po.close()
    po.join()  # 呼叫join之前,先呼叫close函式,否則會出錯。執行完close後不會有新的程序加入到pool,join函式等待所有子程序結束
    print("Sub-process(es) done.")
 

ProcessPoolExecutor:與執行緒池ThreadPoolExecutor 原理及使用方法是一樣的,通過ProcessPoolExecutor建立執行緒池,submit提交任務到執行緒池。max_workers指定執行緒池中的數量

import time
from concurrent.futures import ProcessPoolExecutor


def work(name):
    for i in range(5):
        time.sleep(1)
        print(f"{name}----{i}----")


with ProcessPoolExecutor(max_workers=4) as tp: # 建立程序池
    tp.submit(work, 'MING') # 往程序提交任務
    tp.submit(work, 'HOUX')
	tp.shutdown() # 等待程序池中所有的任務執行完成
    print("---主程序---")
    
執行結果:    
 
MING----0----HOUX----0----

MING----1----HOUX----1----

HOUX----2----MING----2----

MING----3----HOUX----3----

HOUX----4----MING----4----

---主程序---
from:https://blog.csdn.net/weixin_42262081/article/details/120034035