multiprocess(上)

 

仔細說來,multiprocess不是一個模塊而是python中一個操做、管理進程的包。 之因此叫multi是取自multiple的多功能的意思,在這個包中幾乎包含了和進程有關的全部子模塊。因爲提供的子模塊很是多,爲了方便你們歸類記憶,我將這部分大體分爲四個部分:建立進程部分,進程同步部分,進程池部分,進程之間數據共享。重點強調:進程沒有任何共享狀態,進程修改的數據,改動僅限於該進程內,可是經過一些特殊的方法,能夠實現進程之間數據的共享。html

  • Process模塊:一個建立進程的模塊,藉助這個模塊,就能夠完成進程的建立。node

     
     
     
    x
     
     
     
     
    #Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化獲得的對象,表示一個#子進程中的任務(還沒有啓動)
    #強調:
    #1. 須要使用關鍵字的方式來指定參數
    #2. args指定的爲傳給target函數的位置參數,是一個元組形式,必須有逗號
    # 當前文件名稱爲test.py
    from multiprocessing import Process
    def func():
        print(12345)
    if __name__ == '__main__': #windows 下才須要寫這個,這和系統建立進程的機制有關係,不用深究,記着windows下要寫就好啦(由於windows是經過import導入模塊,因此若是不區分那麼會無限導入,而mac、linux則是直接copy導入文件的方式,首先我運行當前這個test.py文件,運行這個文件的程序,那麼就產生了進程,這個進程咱們稱爲主進程
        p = Process(target=func,) #將函數註冊到一個進程中,p是一個進程對象,此時尚未啓動進程,只是建立了一個進程對象。而且func是不加括號的,由於加上括號這個函數就直接運行了對吧。
        p.start() #告訴操做系統,給我開啓一個進程,func這個函數就被咱們新開的這個進程執行了,而這個進程是我主進程運行過程當中建立出來的,因此稱這個新建立的進程爲主進程的子進程,而主進程又能夠稱爲這個新進程的父進程。 而這個子進程中執行的程序,至關於將如今這個test.py文件中的程序copy到一個你看不到的python文件中去執行了,就至關於當前這個文件,被另一個py文件import過去並執行了。
    #   start並非直接就去執行了,咱們知道進程有三個狀態,進程會進入進程的三個狀態,就緒,(被調度,也就是時間片切換到它的時候)執行,阻塞,而且在這個三個狀態之間不斷的轉換,等待cpu執行時間片到了。
        print('*' * 10) #這是主進程的程序,上面開啓的子進程的程序是和主進程的程序同時運行的,咱們稱爲異步
        
    import os
    import time
    from multiprocessing import Process
    def func(a,b,c):
        time.sleep(1)
        print(a,b,c,os.getpid(),os.getppid())     #1 2 3 9936 9592
        # pid : processid   ppid : parent process id
    if __name__ == '__main__':
        # windows操做系統下開啓子進程子進程中的代碼是經過import這種方式被導入到子進程中的
        print('主 :', os.getpid())         #主 : 9592
        Process(target=func,args=(1,2,3)).start()
        
    import time
    import os
    #os.getpid() 獲取本身進程的ID號
    #os.getppid() 獲取本身進程的父進程的ID號
    from multiprocessing import Process
    def func():
        print('aaaa')
        time.sleep(1)
        print('子進程>>',os.getpid())  # 8064
        print('該子進程的父進程>>',os.getppid())  # 3552
        print(12345)
    if __name__ == '__main__':
        #首先我運行當前這個文件,運行的這個文件的程序,那麼就產生了主進程
        p = Process(target=func,)
        p.start()
        print('*' * 10) 
        print('父進程>>',os.getpid())     #3552
        print('父進程的父進程>>',os.getppid())  #10804
        
    #將上個例題的函數下加 print()後會打印兩次,由於子進程會加載主程序的函數,
    #一、也就是說在window是系統上,python經過import導入的形式建立子進程,若是不寫if __name__ 語句,則會無限導入
    #二、if 上面的語句在子進程建立時,會在執行一遍
    def func():
        print('aaaa')
        time.sleep(1)
        print('子進程>>',os.getpid())
        print('該子進程的父進程>>',os.getppid())
        print(12345)
    print('太白老司機~~~~') #若是我在這裏加了一個打印,你會發現運行結果中會出現兩次打印出來的太白老司機,由於咱們在主進程中開了一個子進程,子進程中的程序至關於import的主進程中的程序,那麼import的時候會不會執行你import的那個文件的程序啊,前面學的,是會執行的,因此出現了兩次打印
    #實際上是由於windows開起進程的機制決定的,在linux下是不存在這個效果的,由於windows使用的是process方法來開啓進程,他就會拿到主進程中的全部程序,而linux下只是去執行我子進程中註冊的那個函數,不會執行別的程序,這也是爲何在windows下要加上執行程序的時候,
    #要加上if __name__ == '__main__':,不然會出現子進程中運行的時候還開啓子進程,那就出現無限循環的建立進程了,就報錯了
     
  • 咱們能夠打開windows的任務管理器查看pycharm的pid的進程號:python

    img

  • 一個主進程運行完了以後,咱們把pycharm關了,可是子進程尚未執行結束,那麼子進程還存在嗎?這要看你的進程是如何配置的,若是說咱們沒有配置說我主進程結束,子進程要跟着結束,那麼主進程結束的時候,子進程是不會跟着結束的,他會本身執行完,若是我設定的是主進程結束,子進程必須跟着結束,那麼就不會出現單獨的子進程(孤兒進程)了,具體如何設置,看下面的守護進程的講解。好比說,咱們未來啓動項目的時候,可能經過cmd來啓動,那麼我cmd關閉了你的項目就會關閉嗎,不會的,由於你的項目不能中止對外的服務,對吧,可是全部的孤兒進程會隨着關機,內存釋放。linux

  • Process類中各方法的介紹:web

    •  
       
       
      xxxxxxxxxx
       
       
       
       
      一、 p.start():啓動進程,並調用該子進程中的p.run()
      二、 p.run():進程啓動時運行的方法,正是它去調用target指定的函數,咱們自定義類的類中必定要實現該方法 。
      三、 p.terminate():強制終止進程p,不會進行任何清理操做,若是p建立了子進程,該子進程就成了殭屍進程,使用該方法須要特別當心這種狀況。若是p還保存了一個鎖那麼也將不會被釋放,進而致使死鎖
      四、 p.is_alive():若是p仍然運行,返回True
      五、 p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,須要強調的是,p.join只能join住start開啓的進程,而不能join住run開啓的進程。
      六、start terminate join分別是什麼類型?
          start\terminate 異步非阻塞
          join 同步阻塞
       
  • Process類中自帶封裝的各屬性的介紹算法

     
     
     
    xxxxxxxxxx
     
     
     
     
    1 p.daemon:默認值爲False,若是設爲True,表明p爲後臺運行的守護進程,當p的父進程(代碼終止,而不是主進程終止,由於主進程要進行垃圾回收子進程)終止時,p也隨之終止,而且設定爲True後,p不能建立本身的新進程,必須在p.start()以前設置
    2 p.name:進程的名稱,這些屬性是在Process類中的init方法中實現的
    3 p.pid:進程的pid
    4 p.exitcode:進程在運行時爲None、若是爲–N,表示被信號N結束(瞭解便可)
    5 p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是爲涉及網絡鏈接的底層進程間通訊提供安全性,這類鏈接只有在具備相同的身份驗證鍵時才能成功(瞭解便可)
     
  • join方法的列子:讓主進程加上join的地方等待(也就是阻塞住),等待子進程執行完以後,再繼續往下執行個人主進程,好多時候,咱們主進程須要子進程的執行結果,因此必需要等待。join感受就像是將子進程和主進程拼接起來同樣,將異步改成同步執行。windows

     
     
     
    xxxxxxxxxx
     
     
     
     
    import time
    import os
    from multiprocessing import Process
    def func(x,y):  #前面的省略
        print(x)
        time.sleep(1)
        print(y)
    if __name__ == '__main__':
        p = Process(target=func,args=('姑娘','來玩啊!'))
        p.start()
        print('我這裏是異步的啊!')  #這裏相對於子進程仍是異步的
        p.join()       #只有在join的地方纔會阻塞住,將子進程和主進程之間的異步改成同步
        print('父進程執行結束!')
    #打印結果:
    我這裏是異步的啊!
    姑娘
    來玩啊!
    父進程執行結束!
    #第二種傳參
    def func(a, b, c):
        print("函數func的參數分別是>>>", a, b, c) 
        print("子進程執行完畢!")
    if __name__ == '__main__':
        p = Process(target=func, kwargs={"a":"參數一", "b":"參數二", "c":"參數三"})
        p.start()
        time.sleep(1)
        print("主進程執行完畢!")
    # 執行結果:
    # 函數func的參數分別是>>> 參數一 參數二 參數三
    # 子進程執行完畢!
    # 主進程執行完畢!
    #怎麼樣開啓多個進程呢?for循環。而且我有個需求就是說,全部的子進程異步執行,而後全部的子進程所有執行完以後,我再執行主進程,怎麼搞?看代碼
    def func(x,y):
        print(x)
        # time.sleep(1) #進程切換:若是沒有這個時間間隔,那麼你會發現func執行結果是打印一個x而後一個y,再打印一個x一個y,不會出現打印多個x而後打印y的狀況,由於兩個打印距離太近了並且執行的也很是快,可是若是你這段程序運行慢的話,你就會發現進程之間的切換了。
        print(y)
    if __name__ == '__main__':
        p_list= []
        for i in range(10):
            p = Process(target=func,args=('姑娘%s'%i,'來玩啊!'))
            p_list.append(p)
            p.start()
        [ap.join() for ap in p_list] #四、這是解決辦法,前提是咱們的子進程所有都已經去執行了,那麼我在一次給全部正在執行的子進程加上join,那麼主進程就須要等着全部子進程執行結束纔會繼續執行本身的程序了,而且保障了全部子進程是異步執行的。
           # p.join()放在for內: 若是加到for循環裏面,那麼全部子進程包括父進程就所有變爲同步了,由於for循環也是主進程的,循環第一次的時候,一個進程去執行了,而後這個進程就join住了,那麼for循環就不會繼續執行了,等着第一個子進程執行結束纔會繼續執行for循環去建立第二個子進程。
            #二、若是我不想這樣的,也就是我想全部的子進程是異步的,而後全部的子進程執行完了再執行主進程
        #p.join()在for外部:若是這樣寫的話,屢次運行以後,你會發現會出現主進程的程序比一些子進程先執行完,由於咱們p.join()是對最後一個子進程進行了join,也就是說若是這最後一個子進程先於其餘子進程執行完,那麼主進程就會去執行,而此時若是還有一些子進程沒有執行完,而主進程執行完了,那麼就會先打印主進程的內容了,這個cpu調度進程的機制有關係,由於咱們的電腦可能只有4個cpu,個人子進程加上住進程有11個,雖然我for循環是按順序起進程的,可是操做系統必定會按照順序給你執行你的進程嗎,答案是不會的,操做系統會按照本身的算法來分配進程給cpu去執行,這裏也解釋了咱們打印出來的子進程中的內容也是沒有固定順序的緣由,由於打印結果也須要調用cpu,能夠理解成進程在爭搶cpu,若是同窗你想問這是什麼算法,這就要去研究操做系統啦。那咱們的想全部子進程異步執行,而後再執行主進程的這個需求怎麼解決啊
        print('不要錢~~~~~~~~~~~~~~~~!')
        
    #列:一、同時對一個文件進行寫操做 二、同時建立多個文件
    import time
    import os
    import re
    from multiprocessing import Process
    #多進程同時對一個文件進行寫操做
    def func(x,y,i):
        with open(x,'a',encoding='utf-8') as f:
            print('當前進程%s拿到的文件的光標位置>>%s'%(os.getpid(),f.tell()))
            f.write(y)
    #多進程同時建立多個文件
    # def func(x, y):
    #     with open(x, 'w', encoding='utf-8') as f:
    #         f.write(y)
    if __name__ == '__main__':
        p_list= []
        for i in range(10):
            p = Process(target=func,args=('can_do_girl_lists.txt','姑娘%s'%i,i)) 
            # p = Process(target=func,args=('can_do_girl_info%s.txt'%i,'姑娘電話0000%s'%i))
            p_list.append(p)
            p.start()
        [ap.join() for ap in p_list] #這就是個for循環,只不過用列表生成式的形式寫的
        with open('can_do_girl_lists.txt','r',encoding='utf-8') as f:
            data = f.read()
            all_num = re.findall('\d+',data) #打開文件,統計一下里面有多少個數據,每一個數據都有個數字,因此re匹配一下就好了
            print('>>>>>',all_num,'.....%s'%(len(all_num)))
        #print([i in in os.walk(r'你的文件夾路徑')])
        print('不要錢~~~~~~~~~~~~~~~~!')
      
    #例2terminate關閉子進程
    def func():
        print("子進程開始執行!")
        time.sleep(2)
        print("子進程執行完畢!")
    if __name__ == '__main__':
        p = Process(target=func,)
        p.start()
        p.terminate()  # 給操做系統發送一個關閉進程p1的信號,讓操做系統去關閉它
        time.sleep(1)
        """因爲操做系統關閉子進程的過程須要作許多事情(如回收資源),這是須要耗費必定時間的,
       若是在給操做系統發出關閉信號後(p1.terminate())馬上判斷子進程是否還活着,結果是不許
       確的,此時操做系統正在關閉子進程,因此咱們要等待必定時間才能夠獲得正確的判斷結果."""
        print("子進程是否還活着>>>", p.is_alive())
        print("主進程執行完畢!")
        
     #例3進程對象的其餘方法一:terminate,is_alive
    class Piao(Process):
        def __init__(self,name):
            self.name=name
            super().__init__()
        def run(self):
            print('%s is 打飛機' %self.name)
            # s = input('???') #別忘了再pycharm下子進程中不能input輸入,會報錯EOFError: EOF when reading a line,由於子進程中沒有像咱們主進程這樣的在pycharm下的控制檯能夠輸入東西的地方
            time.sleep(2)
            print('%s is 打飛機結束' %self.name)
    if __name__ == '__main__':
        p1=Piao('太白')
        p1.start()
        time.sleep(5)
        p1.terminate()#關閉進程,不會當即關閉,有個等着操做系統去關閉這個進程的時間,因此is_alive馬上查看的結果可能仍是存活,可是稍微等一會,就被關掉了
        print(p1.is_alive()) #結果爲True
        print('等會。。。。')
        time.sleep(1)
        print(p1.is_alive()) #結果爲False
            
     #例4Process類中自帶的self.name 在父類的init函數在已存在
    class Piao(Process):
        def __init__(self,name):
            self.name=name
            super().__init__() #Process的__init__方法會執行self.name=Piao-1,覆蓋egon
                               #因此加到這裏,會覆蓋咱們的self.name=name
            #爲咱們開啓的進程設置名字的作法
            # super().__init__() # 這種狀況不會影響咱們的名字,不會被覆蓋
            # self.name=name
        def run(self):
            print('%s is piaoing' %self.name)  
            time.sleep(random.randrange(1,3))
            print('%s is piao end' %self.name)
    if __name__=='__main__':
        p=Piao('egon')
        p.start()
        print('開始')
        print(p.pid) #查看pid
    #開始
    #15148
    #Piao-1 is piaoing
    #Piao-1 is piao end
    #例5,子進程的import過程
    from multiprocessing import Process
    import time
    def func():
        print('ok')
    print(1)  #1 第一次打印主程序 打印 1
    if __name__=='__main__':
        p=Process(target=func) #2 開始導入主模塊,import 因此還會執行 打印 1,3
        p.start()  #3 打印 ok
        a=2
        time.sleep(2)
        print('a',a)  #4打印 a ,2
    #若是外部調用a
    # print(a) # 報錯,子進程沒有as
    print('3')  #5 打印3
     
  • 在windows中Process()必須放到# if name == 'main':下安全

     
     
     
    xxxxxxxxxx
     
     
     
     
    因爲Windows沒有fork,多處理模塊啓動一個新的Python進程並導入調用模塊。 
    若是在導入時調用Process(),那麼這將啓動無限繼承的新進程(或直到機器耗盡資源)無限import。 
    這是隱藏對Process()內部調用的原,使用if __name__ == 「__main __」,這個if語句中的語句將不會在導入時被調用。
     
  • 進程建立的第二種方法:(繼承)

     
     
     
    xxxxxxxxxx
     
     
     
     
    class MyProcess(Process): #本身寫一個類,繼承Process類
        #咱們經過init方法能夠傳參數,若是隻寫一個run方法,那麼無法傳參數,由於建立對象的是傳參就是在init方法裏面,面向對象的時候,咱們是否是學過
        def __init__(self,person):
            super().__init__()
            self.person=person
        def run(self):
            print(os.getpid())
            print(self.pid)
            print(self.pid)
            print('%s 正在聊天' %self.person)
            
        # def start(self):
        #     #若是你非要寫一個start方法,能夠這樣寫,而且在run方法先後,能夠寫一些其餘的邏輯
        #     self.run()
    if __name__ == '__main__':
        p1=MyProcess('Jedan')
        p2=MyProcess('太白')
        p3=MyProcess('alexDSB')
        p1.start() #start內部會自動調用run方法,這是在寫了start方法後
        p2.start() 
        # p2.run()
        p3.start()
        p1.join()
        p2.join()
        p3.join()
     
  • 進程的內存空間是隔離的網絡

     
     
     
    xxxxxxxxxx
     
     
     
     
    import time
    from multiprocessing import Process
    global_num = 100
    def func():    # 子進程
        global global_num
        global_num = 0
        print("子進程的全局變量>>>", global_num)
    if __name__ == '__main__':
        p1 = Process(target=func,)
        p1.start()
        time.sleep(1)  # 等待子進程執行結束
        print("主進程的全局變量>>>", global_num)
    # 執行結果:
    # 子進程的全局變量>>> 0
    # 主進程的全局變量>>> 100
    # 得出結論:
    # 進程之間是空間隔離的,不共享資源
     
  • 子進程中不能使用input:若是在子進程中存在input,因爲輸入臺只顯示在主進程中,子進程是沒有輸入臺的,因而系統會自動報錯.因此不要在子進程中出現input。數據結構

  • 殭屍進程和孤兒進程:

    • 殭屍進程(有害):任何一個子進程(init除外)在exit()以後,並不是立刻就消失掉,而是留下一個稱爲殭屍進程(Zombie)的數據結構,等待父進程處理。這是每一個子進程在結束時都要通過的階段。若是子進程在exit()以後,父進程沒有來得及處理,這時用ps命令就能看到子進程的狀態是「Z」。若是父進程能及時 處理,可能用ps命令就來不及看到子進程的殭屍狀態,但這並不等於子進程不通過殭屍狀態。 若是父進程在子進程結束以前退出,則子進程將由init接管。init將會以父進程的身份對殭屍狀態的子進程進行處理。
    • 孤兒進程(無害):一個父進程退出,而它的一個或多個子進程還在運行,那麼那些子進程將成爲孤兒進程。孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工做。孤兒進程是沒有父進程的進程,孤兒進程這個重任就落到了init進程身上,init進程就好像是一個民政局,專門負責處理孤兒進程的善後工做。每當出現一個孤兒進程的時候,內核就把孤 兒進程的父進程設置爲init,而init進程會循環地wait()它的已經退出的子進程。這樣,當一個孤兒進程淒涼地結束了其生命週期的時候,init進程就會表明黨和政府出面處理它的一切善後工做。所以孤兒進程並不會有什麼危害。
  • 守護進程

    • 使用日常的方法時,子進程是不會隨着主進程的結束而結束,只有當主進程和子進程所有執行完畢後,程序纔會結束.可是,若是咱們的需求是: 主進程執行結束,由該主進程建立的子進程必須跟着結束. 這時,咱們就須要用到守護進程了

    • 主進程建立守護進程:

      • 其一: 守護進程會在主進程代碼執行結束後就終止
      • 其二: 守護進程內沒法再開啓子進程,不然拋出異常: AssertionError: daemonic processes are not allowed to have children
      • 須要注意的是: 進程之間是相互獨立的,主進程代碼運行結束,守護進程隨機終止
     
     
     
    xxxxxxxxxx
     
     
     
     
    import os
    import time
    from multiprocessing import Process
    class Myprocess(Process):
        def __init__(self,person):
            super().__init__()
            self.person = person  
        def run(self):
            print("這我的的ID號是:%s" % os.getpid())
            print("這我的的名字是:%s" % self.name)
            time.sleep(3)
    if __name__ == '__main__':
        p=Myprocess('李華')
        p.daemon=True #必定要在p.start()前設置,設置p爲守護進程,禁止p建立子進程,而且父進程代碼執行結束,p即終止運行
        p.start()
        # time.sleep(1) # 在sleep時linux下查看進程id對應的進程ps -ef|grep id
        print('主進程執行完畢!')   #這種狀況子進程都還沒進行就結束了,就打印'主程序……'
     

總結:

一、主進程和子進程互不干擾 二、主進程執行完畢以後程序不會結束,會等待全部的子進程結束以後才結束 三、爲何主進程要等待子進程結束以後才結束? 由於主進程要負責給子進程回收一些系統資源 四、守護進程 : 是一個子進程,守護的是主進程 結束條件 : 主進程的代碼結束,守護進程也結束 五、進程 主進程的代碼結束,守護進程結束 主進程要回收守護進程(子進程)的資源 主進程等待其餘全部子進程結束 主進程回收全部子進程的資源

 

使用原生socket模塊寫一個實現多人聊天的功能?

#服務端
from socket import *
from multiprocessing import Process
def talk(conn,client_addr):
    while True:
        try:
            msg=conn.recv(1024)
            print('客戶端消息>>',msg)
            if not msg:break
            conn.send(msg.upper())
            #在這裏有同窗可能會想,我能不能在這裏寫input來本身輸入內容和客戶端進行對話?朋友,是這樣的,按說是能夠的,可是須要什麼呢?須要你像咱們用pycharm的是同樣下面有一個輸入內容的控制檯,當咱們的子進程去執行的時候,咱們是沒有地方能夠顯示可以讓你輸入內容的控制檯的,因此你沒辦法輸入,就會給你報錯。
        except Exception:
            break

if __name__ == '__main__': #windows下start進程必定要寫到這下面
    server = socket(AF_INET, SOCK_STREAM)
    # server.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)  # 若是你將若是你將bind這些代碼寫到if __name__ == '__main__'這行代碼的上面,那麼地址重用必需要有,由於咱們知道windows建立的子進程是對整個當前文件的內容進行的copy,前面說了就像import,若是你開啓了子進程,那麼子進程是會執行bind的,那麼你的主進程bind了這個ip和端口,子進程在進行bind的時候就會報錯。
    server.bind(('127.0.0.1', 8080))
    #有同窗可能還會想,我爲何多個進程就能夠鏈接一個server段的一個ip和端口了呢,我記得當時說tcp的socket的時候,我是不能在你這個ip和端口被鏈接的狀況下再鏈接你的啊,這裏是由於當時咱們就是一個進程,一個進程裏面是隻能一個鏈接的,多進程是能夠多鏈接的,這和進程之間是單獨的內存空間有關係,先這樣記住他,好嗎?
    server.listen(5)
    while True:
        conn,client_addr=server.accept()
        p=Process(target=talk,args=(conn,client_addr))
        p.start()

#客服端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))

while True:
    msg=input('>>: ').strip()
    if not msg:continue

    client.send(msg.encode('utf-8'))
    msg=client.recv(1024)
    print(msg.decode('utf-8'))
相關文章
相關標籤/搜索