1. 程式人生 > 其它 >建立程序,join方法,程序物件相關屬性和方法,殭屍程序和孤兒程序,守護程序,互斥鎖

建立程序,join方法,程序物件相關屬性和方法,殭屍程序和孤兒程序,守護程序,互斥鎖

建立程序

    在python中提供了一個multiprocessing模組可以幫助我們使用多程序解決問題。在multiprocessing
模組中有一個類Process。
    from multiprocessing import Process

'''
    group=None, 為日後開發新功能準備 
    target=None, 目標任務
    name=None, 程序的姓名
    args=(), 目標任務需要的位置引數
    kwargs={}, 目標任務需要的字典引數
    daemon=None):預設值為False,如果設為True,代表p為後臺執行的守護程序,當p的父程序終止時,p也隨之終止,並且設定為True後,p不能建立自己的新程序,必須在p.start()之前設定
    '''
    #方法介紹
    p.start()#開啟程序,使用run()方法
    p.run()#程序啟動時執行的方法是它去呼叫target指定的目標
    p.terminate()#強制終止程序,不會進行清理操作
    p.is_alive()#判斷程序是否存活
    p.join([timeout])#讓在join方法之後的程序等待,直到p程序執行結束

"""
強調:不同的作業系統建立程序的要求不一樣
    在windows中建立程序是以匯入模組的方式進行 所以建立程序的程式碼必須寫在__main__子程式碼中
    否則會直接報錯 因為在無限制建立程序
    在linux和mac中建立程序是直接拷貝一份原始碼然後執行 不需要寫在__main__子程式碼中
"""

第一種方法

    from multiprocessing import Process
    import time


    def test():
        print('=======>')
        time.sleep(3)
        print('=======>')


    def test1(name):
        print(f'{name}')
        time.sleep(3)
        print('=======>')


    if __name__ == '__main__':
        p = Process(target=test)
        # p = Process(target=test1, args=('春遊去動物園',))
        p.start()
        print('主')

第二種方法

    from multiprocessing import Process

    class Test(Process):
        def __init__(self,name):
            super().__init__()
            self.name = name


        def run(self):
            print(self.name)


    if __name__ == '__main__':
        p = Test('test')
        p.start()
        print('主')
    #自定義類主要是改寫他的run()方法,run()方法在呼叫start()方法時一定會被呼叫

join

'''join讓主程序等待子程序結束之後,再執行主程序。'''

    from multiprocessing import Process
    import time
    def task(name):
        print(f"{name} is running")
        time.sleep(2)
        print(f"{name} is gone")
    if __name__ == "__main__":
        p = Process(target=task, args=("春遊去動物園",))  # 建立一個程序物件
        p.start()
        # p.join()
        print(111)
        print("主開始")

    111
    主開始
    春遊去動物園 is running
    春遊去動物園 is gone


    from multiprocessing import Process
    import time
    def task(name):
        print(f"{name} is running")
        time.sleep(2)
        print(f"{name} is gone")
    if __name__ == "__main__":
        p = Process(target=task, args=("春遊去動物園",))  # 建立一個程序物件
        p.start()
        p.join()
        print(111)
        print("主開始")
        
    春遊去動物園 is running
    春遊去動物園 is gone
    111
    主開始
'''一般而言,主程式中如果單為一句print,則優先執行print語句(如果執行語句夠多,則可見子程序執行)'''

    from multiprocessing import Process
    import time

    def task(name):
        print(f"{name} is running")
        time.sleep(2)
        print(f"{name} is gone")

    if __name__ == "__main__":
        p = Process(target=task,args=("春遊去動物園",))  # 建立一個程序物件
        p.start()
        # p.join()
        print(111)
        time.sleep(0.5)
        print("主開始")
        
    111
    春遊去動物園 is running
    主開始
    春遊去動物園 is gone
'''如果程式中有連續多個join函式,則只有最先的join是起作用的'''
    from multiprocessing import Process
    import time

    def task(name,sec):
        print(f"{name} is running")
        time.sleep(sec)
        print(f"{name} is gone")

    if __name__ == "__main__":
        start_time = time.time()
        p = Process(target=task,args=("常",3))  # 建立一個程序物件
        p1 = Process(target=task,args=("辛",5))  # 建立一個程序物件
        p2 = Process(target=task,args=("王",1))  # 建立一個程序物件

        p.start()
        p1.start()
        p2.start()

        # join只針對主程序,如果join下面多次join他是不阻塞的
        p.join()
        p1.join()
        p2.join()

        print(f"主{time.time()-start_time}")

    辛 is running
    常 is running
    王 is running
    王 is gone
    常 is gone
    辛 is gone
    主5.103694438934326



    # 注:如果不是連續多個,比如這樣:
    from multiprocessing import Process
    import time

    def task(name,sec):
        print(f"{name} is running")
        time.sleep(sec)
        print(f"{name} is gone")

    if __name__ == "__main__":
        start_time = time.time()
        p = Process(target=task,args=("常",3))  # 建立一個程序物件
        p1 = Process(target=task,args=("辛",5))  # 建立一個程序物件
        p2 = Process(target=task,args=("王",1))  # 建立一個程序物件

        p.start()
        p.join()
        p1.start()
        p1.join()
        p2.start()
        p2.join()

        print(f"主{time.time()-start_time}")

    常 is running
    常 is gone
    辛 is running
    辛 is gone
    王 is running
    王 is gone
    主9.267089128494263
    # 這樣就會先執行p程序,再執行p1程序,再執行p2程序

程序間資料預設隔離

    from multiprocessing import Process

    a=100
    def test():
        global a
        a=50
        print(a) # 50


    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(a) # 100

    100
    50

程序物件屬性和方法

檢視程序號

    '''第一種current_process函式'''
    from multiprocessing import Process,current_process
    a=100
    def test():
        global a
        a=50
        print(a) # 50
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(current_process()) # 獲取程序的名稱 <_MainProcess name='MainProcess' parent=None started>
        print(current_process().pid) # 獲取程序的id 8560
        print(a) # 100

    '''第二種os模組'''
    import os
    from multiprocessing import Process,current_process
    a=100
    def test():
        global a
        a=50
        print(a) # 50
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(os.getpid()) # 9748 獲取當前程序的程序號
        print(os.getppid()) # 5220 獲取當前程序的父程序號
        print(a) # 100


    os.getpid()  # 獲取當前程序的程序號
    os.getppid()  # 獲取當前程序的父程序號

殺死子程序

    terminate():不管任務是否完成,立即終止子程序
    from multiprocessing import Process,current_process
    import time
    a=100
    def test():
        global a
        time.sleep(3)
        a=50
        print(a)
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        p.terminate()
        print(a)
    100

判斷子程序是否存活

    is_alive():判斷程序子程序是否還在活著
    from multiprocessing import Process,current_process
    import time
    a=100
    def test():
        global a
        time.sleep(3)
        a=50
        print(a)
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(p.is_alive()) # True
        p.terminate() # 殺死程序
        time.sleep(0.5)
        print(p.is_alive()) # False

殭屍程序與孤兒程序

殭屍程序

  殭屍程序:一個程序使用fork建立子程序,如果子程序退出,而父程序並沒有呼叫wait或waitpid獲取子
程序的狀態資訊,那麼子程序的程序描述符仍然儲存在系統中。這種程序稱之為僵死程序。
    '''
        僵死程序:子程序退出後,會將該程序的重型資源釋放掉(cpu,記憶體,開啟的檔案),子程序的進
程描述符仍然儲存在系統中,比如pid。
    '''
    所有的子程序在執行結束之後都會變成殭屍程序(死了沒死透)
    程式正常結束才會產生殭屍程序,如果強制關閉父程序,作業系統會把父程序已經執行結束的子程序全部
刪除,也就不會產生殭屍程序了。
    殭屍程序的危害:
      系統的pid號是有限的,殭屍程序保留的資訊如果一直不被釋放,一直累計會導致沒有可用的pid號而
導致系統不能產生新的程序

孤兒程序

   孤兒程序(無害):一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒
程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。
   '''
      子程序存活著 父程序意外死亡
      子程序會被作業系統自動接管(兒童福利院)
   '''

守護程序

    正常情況下,主程序預設等待子程序呼叫結束之後結束
    守護程序在主程序執行程式碼結束後,自動終止
    """
        守護即死活全部參考守護的物件
	物件死立刻死
    """
    主程序程式碼執行完畢,守護程序就會結束
    from multiprocessing import Process
    import os, time


    def task():
        print("程序%s開啟" % os.getpid())
        time.sleep(10)
        print("程序%s結束" % os.getpid())


    if __name__ == '__main__':
        p = Process(target=task,daemon=True) # 在建立程序時也可以設定daemon,True為開啟守護程序,預設為False。
        #p.daemon = True   	# 這一行程式碼會把子程序變成守護程式碼,主程序執行完,子程序也就執行完了,不會列印程序結束的那行程式碼
        p.start()
        print("主:%s" % os.getpid())
        time.sleep(3)

        主:6812
        程序10096開啟

互斥鎖

互斥鎖的概念

    互斥鎖: 對共享資料進行鎖定,保證同一時刻只能有一個執行緒去操作。

    注意:
      互斥鎖是多個執行緒一起去搶,搶到鎖的執行緒先執行,沒有搶到鎖的執行緒需要等待,等互斥鎖使用完釋放
後,其它等待的執行緒再去搶這個鎖。

    互斥鎖的意思就是互相排斥,我們可以把多個程序比喻多個人,互斥鎖的工作原理就是多個人去爭搶共同
的一個資源:如多個人要上同一個衛生間,一個人搶到衛生間後上一把鎖,其他人都有外面等著,等到這個人
完成後解鎖後,其他人又可以去爭奪。所以互斥鎖的原題,就是把某一功能併發改序列,雖然降低了效率,但
保證了資料安全不錯亂。
    """
    鎖相關知識
    	行鎖:針對行資料加鎖 同一時間只能一個人操作
    	表鎖:針對表資料加鎖 同一時間只能一個人操作
    鎖的應用範圍很廣 但是核心都是為了保證資料的安全!!!
    """

互斥鎖的使用

    from multiprocessing import Process,Lock
    # 建立鎖
    mutex = Lock()

    # 上鎖
    mutex.acquire()

    ...這裡編寫程式碼能保證同一時刻只能有一個執行緒去操作, 對共享資料進行鎖定...

    # 釋放鎖
    mutex.release()

    注意點:
    acquire和release方法之間的程式碼同一時刻只能有一個執行緒去操作
    如果在呼叫acquire方法的時候 其他執行緒已經使用了這個互斥鎖,那麼此時acquire方法會堵塞,直到
這個互斥鎖釋放後才能再次上鎖。

互斥鎖與join()的區別

    互斥鎖與join()的區別:
      大前提:二者的原理都是一樣,都是將併發變成序列,從而保證有序

      區別一:join是按照人為指定的順序執行,而互斥鎖是所有程序平等地競爭,誰先搶到誰先執行。

      區別二:互斥鎖可以讓一部分程式碼(修改共享資料的程式碼)序列,而join只能將程式碼整體序列。

搶票小案例

    # db.json檔案中的內容為{"count": 100}
    from multiprocessing import Process, Lock
    import json, time
    import random


    def search(name):
        dic = json.load(open('db.json', 'r', encoding='utf-8'))
        time.sleep(random.randint(0, 2))  # 模擬網路延遲
        print('%s查到剩餘的票數為%s張' % (name, dic['count']))


    def get(name):
        dic = json.load(open('db.json', 'r', encoding='utf-8'))
        time.sleep(0.3)  # 模擬網路延遲
        if dic['count'] > 0:
            time.sleep(0.1)  # 模擬網路延遲
            print('%s成功買到了剩下的第%s票' % (name, dic['count']))
            dic['count'] -= 1
            json.dump(dic, open('db.json', 'w', encoding='utf-8'))


    def rob_ticket(name, lock):
        search(name)
        with lock:  # 相當於獲得了lock.acquire(),執行程式碼體完,自動執行lock.release()
            get(name)


    if __name__ == '__main__':
        lock = Lock()
        for i in range(10):
            name = '路人%s' % i
            p = Process(target=rob_ticket, args=(name, lock))
            p.start()
            
    執行結果:
    路人0查到剩餘的票數為5張
    路人1查到剩餘的票數為5張
    路人8查到剩餘的票數為5張
    路人7查到剩餘的票數為5張
    路人9查到剩餘的票數為5張
    路人0成功買到了剩下的第5票
    路人1成功買到了剩下的第4票
    路人2查到剩餘的票數為5張
    路人4查到剩餘的票數為5張
    路人3查到剩餘的票數為5張
    路人6查到剩餘的票數為5張
    路人8成功買到了剩下的第3票
    路人7成功買到了剩下的第2票
    路人5查到剩餘的票數為5張
    路人9成功買到了剩下的第1票