Python中的多程式

程式設計師小城發表於2019-03-15

多程式:

程式:是一個指令的集合

程式:正在執行的程式,或者說當你執行一個程式,你就啟動了一個程式。

--編寫完的程式碼,沒有執行時稱為程式,正在執行的程式碼,稱為程式。

--程式是死的(靜態的)程式是活的(動態的)

作業系統輪流讓各個任務交替執行,由於CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。

多程式中,每個程式中所有資料(包括全域性變數)都各自擁有一份,互不影響。

例如:我們啟動了QQ,QQ就是一個主程式,在QQ執行過程中,我們分別開啟了三個(和A B C同學的)聊天視窗,這時 每個視窗就是屬於主程式下的一個子程式,我們和每個同學聊天的資料都各有一份,互不影響。

#模擬多工處理:一邊唱歌,一邊跳舞
from time import sleep
def sing():
    for i in range(5):
        print("唱歌")
        dance()
        sleep(1)
def dance():
    print("跳舞")
sing()

程式開始執行時,首先會建立一個主程式,在主程式下,我們可以建立新的程式(子程式),子程式依賴於主程式,如果主程式結束,程式會退出。(例如:QQ執行過程中(主程式)我們開啟了和多位好友的聊天視窗(子程式),和他們聊天,這時如果直接退出QQ,聊天視窗也會消失。)

Python提供了非常好用的多程式包multiprocessing,藉助這個包,可以輕鬆完成從單程式到併發執行的轉換。

multiprocessing模組提供了一個Process類來建立一個程式物件。

(也就是說Process類中把程式物件該有的特徵都寫入了,有了Process類,方便我們建立程式物件)

Process(target,name,args)

引數介紹:

-target 表示呼叫物件,即子程式要執行的任務

-args 表示呼叫物件的位置引數元組

-name 子程式的名稱

from multiprocessing import Process
def run(name):
    print("子程式執行中,name=%s"%name)
    print("子程式結束")
    # print(a)
if __name__ == '__main__':
    print("父程式啟動")
    # a = 1000
    p=Process(target=run,args=('test',),name='pro-1')
    #"="右邊是建立的子程式物件,p是該物件的引用  name='pro-1'可以不寫,系統會預設起名: Process-n(是第幾個子程式就叫-幾)
    #target表示呼叫物件,args表示呼叫物件的位置引數元組
    #注意:元組中只有一個元素時結尾要加,
    print("子程式將要執行")
    p.daemon=True  #是否設定為守護程式(主程式結束,子程式也結束)(如果主程式結束,子程式還在執行,那麼這個子程式叫孤兒程式)
    p.start()#此時子程式執行,CPU中主程式和子程式頻繁做切換
    #在子程式執行時有個問題:在Windows上,子程式執行時會自動import啟動它的這個檔案(主程式),而在import的時候是會執行這些語句的,
    #這些語句中包含了建立一個子程式物件,因此會導致遞迴(無限建立子程式)
    print(p.name)
    p.join()#此處join()的作用:讓主程式等子程式執行完再退出(因為程式執行過程中如果主程式退出,此時子程式未執行完,也會退出)
    print("主程式結束")
'''
if __name__=='__main__':說明
一個Python的檔案(模組)有兩種使用的方法,第一是直接作為程式執行,
第二是被其他Python模組匯入(import)呼叫執行(模組重用)
因此if __name__=='__main__':的作用就是控制這兩種情況執行程式碼的過程,__name__是內建變數,用於表示當前模組的名字
(如果當前的檔案作為程式直接執行,那麼此時它的__name__就是__main)也就是說
在if __name__=='__main__':下的程式碼只有在檔案作為程式直接執行時才會執行,而import到其他程式中是不會執行的
在Windows上,子程式會自動import啟動它的這個檔案,而在import的時候是會執行這些語句的,如果不加if __name__=='__main__':
的話就會無線遞迴建立子程式,所以必須把建立子程式的部分用那個if判斷保護起來
import的時候如果__name__不是__main__,就不會遞迴執行了
'''

Process類常用方法:

—p.start() 啟動程式,並呼叫該子程式中的p.run()

—p.run() 程式啟動時執行的方法,正是它去呼叫target指定的函式

—p.terminate() 強制終止程式p,不會進行任何清理操作(瞭解即可)

程式強制終止後,程式物件仍然存在,但是此時你卻不能調(因為已經強制終止了,不能再用start()了),此時的程式物件也被稱為殭屍程式。

例如:如果程式有個限制:程式1執行完後才能執行程式2,此時把程式1強制終止,那麼程式2永遠也不能執行了。

—p.is_alive() 如果p仍然執行,返回True.用來判斷程式是否還在執行

—p.join([timeout]):主程式等待p終止,timeout是可選的超時時間

Process類常用屬性:

name:當前程式例項別名,預設為Pricess-N,N為從1開始遞增的整數;

pid:當前程式例項的PID值(也就是當前的一個程式號)(只要有一個程式執行,系統就會給它分配一個PID,是系統用來分辨不同的程式用的)

全域性變數在多個程式中不共享,程式之間的資料是獨立的,預設情況下互不影響。

from multiprocessing import Process
num=1
def run1():
    global num
    num+=5
    print("子程式1執行中,num=%d"%num)
def run2():
    global num
    num+=10
    print("子程式2執行中,num=%d"%num)
if __name__ == '__main__':
    print("主程式啟動")
    p1=Process(target=run1)
    p2=Process(target=run2)
    print("子程式將要執行")
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("子程式結束")
#執行結果
主程式啟動
子程式將要執行
子程式1執行中,num=6
子程式2執行中,num=11
子程式結束
1

建立新的程式還能夠使用類的方式,可以自定義一個類,繼承Process類,每次例項化這個類的時候,就等同於例項化一個程式物件.

import multiprocessing,time
class ClockProcess (multiprocessing.Process):
    def run(self):#重寫run()方法
        n=5
        while n>0:
            print(n)
            time.sleep(1)
            n-=1
if __name__ == '__main__':
    p=ClockProcess()
    p.start()#啟動程式並呼叫run()方法
    p.join()

程式池:

程式池:用來建立多個程式

當需要建立的⼦程式數量不多時, 可以直接利⽤multiprocessing中的Process動態生成多個程式, 但如果是上百甚⾄上千個⽬標,⼿動的去建立程式的⼯作量巨⼤,此時就可以⽤到multiprocessing模組提供的Pool

初始化Pool時,可以指定⼀個最⼤程式數,當有新的請求提交到Pool中時,如果池還沒有滿,那麼就會建立⼀個新的程式⽤來執⾏該請求,但如果池中的程式數已經達到指定的最⼤值,那麼該請求就會等待,直到池中有程式結束才會建立新的程式來執⾏.

from multiprocessing import Pool
import random,time
def work(num):
    print(random.random()*num)
    time.sleep(3)
if __name__ == '__main__':
    po=Pool(3)#定義一個程式池,最大程式數為3 預設大小為CPU核數
    for i in range(10):
        po.apply_async(work,(i,))#apply_async選擇要呼叫的目標,每次迴圈會用空出來的子程式去呼叫目標
    po.close()#程式池關閉之後不再接受新的請求
    po.join()#等待po中所有子程式結束,語法規定:必須放在close後面
#在多程式中,主程式一般用來等待,真正的任務都在子程式中執行

multiprocessing.Pool常⽤函式解析:

–apply_async(func[, args[, kwds]]) : 使⽤⾮阻塞⽅式調⽤func(並⾏執⾏,堵塞⽅式必須等待上⼀個程式退出才能執⾏下⼀個程式)args為傳遞給func的引數列表,kwds為傳遞給func的關鍵字引數列表;

–apply(func[, args[, kwds]])(瞭解即可幾乎不用) 使⽤阻塞⽅式調⽤func

–close():關閉Pool,使其不再接受新的任務;

–terminate():不管任務是否完成,⽴即終⽌;

join():主程式阻塞,等待⼦程式的退出, 必須在close或terminate之後 使⽤;

相關文章