1. 程式人生 > >python大佬養成計劃-----多程序

python大佬養成計劃-----多程序

程序

Python是執行在直譯器中的語言,查詢資料知道,python中有一個全域性鎖(GIL),在使用多程序(Thread)的情況下,不能發揮多核的優勢。而使用多程序(Multiprocess),則可以發揮多核的優勢真正地提高效率。如果多執行緒的程序是CPU密集型的,那多執行緒並不能有多少效率上的提升,相反還可能會因為執行緒的頻繁切換,導致效率下降,推薦使用多程序;如果是IO密集型,多執行緒的程序可以利用IO阻塞等待時的空閒時間執行其他執行緒,提升效率。

建立程序

linux和mac系統特殊方法

1.Linux建立子程序的原理:1). 父程序和子程序, 如果父程序結束, 子程序也隨之結束;2). 先有父程序, 再有子程序, 通過fork函式實現;

2.fork函式的返回值:呼叫該方法一次, 返回兩次;

  • 產生的子程序返回一個0
  • 父程序返回子程序的pid;

3.Window也能使用fork函式麼?

Windows沒有fork函式, Mac有fork函式(Unix -> Linux, Unix-> Mac),
封裝了一個模組multiprocessing

4.常用方法:

  • os.fork()
  • os.getpid(): 獲取當前程序的pid;
  • os.getppid(): parent process id, 獲取當前程序的父程序的id號;
import  os
import  time
print("當前程序(pid=%d)正在執行..." %(os.getpid()))
print("當前程序的父程序(pid=%d)正在執行..." %(os.getppid()))
print("正在建立子程序......")
pid = os.fork()
pid2 = os.fork()
print("第1個:", pid)
print("第2個: ", pid2)

if pid == 0:
    print("這是建立的子程序, 子程序的id為%s, 父程序的id為%s"
          %(os.getpid(), os.getppid()))
else:
    print("當前是父程序[%s]的返回值%s" %(os.getpid(), pid))
time.sleep(100)

win系統

在win系統下,使用例項化multiprocessing.Process建立程序須新增'if __name__=="__main__"',否則會出現以下報錯:

RuntimeError:

An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.

This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:

   if __name__ == '__main__':
       freeze_support()
       ...

The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
import  multiprocessing
def job():
    print("當前子程序的名稱為%s" %(multiprocessing.current_process()))
if __name__=="__main__":    #win作業系統需要加上,否則會出現異常報錯RuntimeError

    # 建立一個程序物件(group=None, target=None, name=None, args=(), kwargs={})
    p1 = multiprocessing.Process(target=job)
    p2 = multiprocessing.Process(target=job)
    # 執行多程序, 執行任務
    p1.start()
    p2.start()

    # 等待所有的子程序執行結束, 再執行主程序的內容
    p1.join()
    p2.join()
    print("任務執行結束.......")

圖片描述

通過重寫multiprocessing.Process類建立多程序

from multiprocessing import Process
import multiprocessing
class JobProcess(Process):
    # 重寫Process的構造方法, 獲取新的屬性
    def __init__(self,queue):
        super(JobProcess, self).__init__()
        self.queue = queue
    # 重寫run方法, 將執行的任務放在裡面即可
    def run(self):
        print("當前程序資訊%s" %(multiprocessing.current_process()))

if __name__=="__main__":
    processes = []
    # 啟動10個子程序, 來處理需要執行的任務;
    for i in range(10):
        #示例化類,建立程序
        p = JobProcess(queue=3)
        processes.append(p)
        #啟動多程序,執行任務
        p.start()
    #等待所有的子程序結束,再執行主程序
    [pro.join() for pro in processes]
    print("任務執行結束")

圖片描述

守護程序

守護執行緒:

setDeamon:
    True: 主執行緒執行結束, 子執行緒不再繼續執行;
    Flase:

守護程序:

  setDeamon:
    True: 主程序執行結束, 子程序不再繼續執行;
    Flase:
import multiprocessing
import time
def deamon():
    #守護程序:當主程式執行結束,子程序也結束
    name = multiprocessing.current_process()
    print("%s開始執行" %(name))
    time.sleep(3)
    print("執行結束")

if __name__=="__main__":
    p1 = multiprocessing.Process(target=deamon,name='hello')
    p1.daemon = True
    p1.start()
    time.sleep(2)
    print("整個程式執行結束")

圖片描述

終止程序

有些程序或許再執行死迴圈任務,此時我們手動結束程序terminate()

import multiprocessing
import time

def job():
    name = multiprocessing.current_process()
    print("%s程序開啟" %(name))
    time.sleep(3)
    print("程序結束")

if __name__=="__main__":
    p = multiprocessing.Process(target=job)
    print("程序開啟:",p.is_alive())
    p.start()
    print("程序開啟:",p.is_alive())
    p.terminate()
    print("程序開啟:",p.is_alive())
    time.sleep(0.001)
    print("程序開啟:",p.is_alive())
    print("程式執行結束")

圖片描述

計算密集型和I/O密集型

計算密集型任務的特點是要進行大量的計算, 消耗CPU資源, 比如計算圓周率、 對視訊進行高清解碼等等, 全靠CPU的運算能力。 這種計算密集型任務雖然也可以用多工完成, 但是任務越多, 花在任務切換的時間就越多, CPU執行任務的效率就越低, 所以, 要最高效地利用CPU, 計算密集型任務同時進行的數量應當等於CPU的核心數。計算密集型任務由於主要消耗CPU資源, 因此, 程式碼執行效率至關重要。 Python這樣的指令碼語言執行效率很低, 完全不適合計算密集型任務。 對於計算密集型任務,最好用C語言編寫。

第二種任務的型別是IO密集型, 涉及到網路、 磁碟IO的任務都是IO密集型任務, 這類任務的特點是CPU消耗很少, 任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和記憶體的速度)。對於IO密集型任務, 任務越多, CPU效率越高, 但也有一個限度。 常見的大部分任務都是IO密集型任務, 比如Web應用。

多程序和多執行緒對比

多程序模式最大的優點就是穩定性高, 因為一個子程序崩潰了, 不會影響主程序和其他子程序。(當然主程序掛了所有程序就全掛了, 但是Master程序只負責分配任務, 掛掉的概率低)著名的Apache最早就是採用多程序模式。

多程序模式的缺點是建立程序的代價大, 在Unix/Linux系統下, 用 fork 呼叫還行, 在Windows下建立程序開銷巨大。 另外, 作業系統能同時執行的程序數也是有限的, 在記憶體和。CPU的限制下, 如果有幾千個程序同時執行, 作業系統連排程都會成問題。

多執行緒模式通常比多程序快一點, 但是也快不到哪去, 而且, 多執行緒模式致命的缺點就是任何一個執行緒掛掉都可能直接造成整個程序崩潰, 因為所有執行緒共享程序的記憶體。 在Windows上, 如果一個執行緒執行的程式碼出了問題, 你經常可以看到這樣的提示:“該程式執行了非法操作, 即將關閉”, 其實往往是某個執行緒出了問題, 但是作業系統會強制結束整個程序。這裡通過一個計算密集型任務,來測試多程序和多執行緒的執行效率。

import multiprocessing
import threading
from mytimeit import timeit

class JobProcess(multiprocessing.Process):
    def __init__(self,li):
        super(JobProcess, self).__init__()
        self.li = li
    def run(self):
        for i in self.li:
            sum(i)


class JobThread(threading.Thread):
    def __init__(self,li):
        super(JobThread, self).__init__()
        self.li = li
    def run(self):
        for i in self.li:
            sum(i)
@timeit
def many_processs():
    li = [[24892,23892348,239293,233],[2382394,49230,2321234],[48294,28420,29489]]*10
    processes = []
    for i in li :
        p = JobProcess(li)
        processes.append(p)
        p.start()
    [pro.join() for pro in processes]
    print("多程序執行任務結束,✌")
@timeit
def many_thread():
    #建立程序和銷燬程序是時間的,如果li長度不夠,會造成多執行緒快過多程序
    li = [[24892,23892348,239293,233],[2382394,49230,2321234],[48294,28420,29489]]*1000
    threads = []
    for i in li :
        t = JobThread(li)
        threads.append(t)
        t.start()
    [thread.join() for thread in threads]
    print("多執行緒執行任務結束,✌")
if __name__ =="__main__":
    many_processs()
    many_thread()

圖片描述

程序間通訊-生產者消費者模型與佇列

演示了生產者和消費者的場景。生產者生產貨物,然後把貨物放到一個佇列之類的資料結構中,生產貨物所要花費的時間無法預先確定。消費者消耗生產者生產的貨物的時間也是不確定的。通過佇列來實現程序間的通訊

import multiprocessing
import threading
from multiprocessing import Queue

class Producer(multiprocessing.Process):
    def __init__(self,queue):
        super(Producer, self).__init__()
        self.queue = queue
    def run(self):
        for i in range(13):
            #往佇列新增內容
            self.queue.put(i)
            print("生產者傳遞的訊息為%s" %(i))
        return self.queue

class Consumer(multiprocessing.Process):
    def __init__(self,queue):
        super(Consumer, self).__init__()
        self.queue = queue
    def run(self):
        #獲取佇列內容
        #get會自動判斷佇列是否為空,如果是空, 跳出迴圈, 不會再去從佇列獲取資料;
        while True:
            print("程序獲取訊息為:%s" %(self.queue.get()))
if __name__=="__main__":
    queue  = Queue(maxsize=100)
    p = Producer(queue)
    p.start()
    c = Consumer(queue)
    c.start()
    p.join()
    c.join(2)
    c.terminate()   #終止程序
    print("程序間通訊結束,( •̀ ω •́ )y")

圖片描述