110 python程序中的進程操做-開啓多進程

以前咱們已經瞭解了不少進程相關的理論知識,瞭解進程是什麼應該再也不困難了,剛剛咱們已經瞭解了,運行中的程序就是一個進程。全部的進程都是經過它的父進程來建立的。所以,運行起來的python程序也是一個進程,那麼咱們也能夠在程序中再建立進程。多個進程能夠實現併發效果,也就是說,當咱們的程序中存在多個進程的時候,在某些時候,就會讓程序的執行速度變快。以咱們以前所學的知識,並不能實現建立進程這個功能,因此咱們就須要藉助python中強大的模塊。python

1、multiprocess模塊

仔細說來,multiprocess不是一個模塊而是python中一個操做、管理進程的包。 之因此叫multi是取自multiple的多功能的意思,在這個包中幾乎包含了和進程有關的全部子模塊。因爲提供的子模塊很是多,爲了方便你們歸類記憶,我將這部分大體分爲四個部分:建立進程部分,進程同步部分,進程池部分,進程之間數據共享。linux

2、multiprocess.process模塊

process模塊是一個建立進程的模塊,藉助這個模塊,就能夠完成進程的建立。windows

3、process模塊介紹

Process([group [, target [, name [, args [, kwargs]]]]]),由該類實例化獲得的對象,表示一個子進程中的任務(還沒有啓動)安全

強調:網絡

  1. 須要使用關鍵字的方式來指定參數
  2. args指定的爲傳給target函數的位置參數,是一個元組形式,必須有逗號

參數介紹:併發

  • group參數未使用,值始終爲None
  • target表示調用對象,即子進程要執行的任務
  • args表示調用對象的位置參數元組,args=(1,2,'egon',)
  • kwargs表示調用對象的字典,kwargs={'name':'egon','age':18}
  • name爲子進程的名稱

3.1方法介紹

  • 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開啓的進程

3.2屬性介紹

  • p.daemon:默認值爲False,若是設爲True,表明p爲後臺運行的守護進程,當p的父進程終止時,p也隨之終止,而且設定爲True後,p不能建立本身的新進程,必須在p.start()以前設置
  • p.name:進程的名稱
  • p.pid:進程的pid
  • p.exitcode:進程在運行時爲None、若是爲–N,表示被信號N結束(瞭解便可)
  • p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是爲涉及網絡鏈接的底層進程間通訊提供安全性,這類鏈接只有在具備相同的身份驗證鍵時才能成功(瞭解便可)

3.3在windows中使用process模塊的注意事項

在Windows操做系統中因爲沒有fork(linux操做系統中建立進程的機制),在建立子進程的時候會自動 import 啓動它的這個文件,而在 import 的時候又執行了整個文件。所以若是將process()直接寫在文件中就會無限遞歸建立子進程報錯。因此必須把建立子進程的部分使用if __name__ =='__main__' 判斷保護起來,import 的時候,就不會遞歸運行了。app

4、使用process模塊建立進程

在一個python進程中開啓子進程,start方法和併發效果。dom

4.1在python中啓動的第一個子進程

# 沒有參數的,開啓單個子進程
from multiprocessing import Process
import  time

def talk():
    print('開啓子進程start')
    time.sleep(1)
    print('結束子進程end')

if __name__ == '__main__':
     p = Process(target=talk)
     p.start() # 告訴操做系統,我要開啓子進程,僅僅是開啓子進程就完了,接着走下面的代碼,具體操做系統何時開是操做系統的事情
     print('主進程/父進程')

主進程/父進程
開啓子進程start
結束子進程endsocket

# 帶有參數的,開啓多個子進程
def talk(name):
    print(f'{name}子進程start')
    time.sleep(1)
    print(f'{name}子進程end')

if __name__ == '__main__':
    p = Process(target=talk,args=('xichen',))
    p1 = Process(target=talk,args = ('nick',))
    p.start()
    p1.start()
    print('主進程/父進程')

主進程/父進程
xichen子進程start
nick子進程start
xichen子進程end
nick子進程end函數

注意:開啓多個進程的時候,操做系統先開啓哪個進程是咱們控制不了的,這是操做系統本身所擁有的調度機制

4.2繼承Process來開啓進程

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

    def run(self):
        print(f'子進程{self.name}-->start')
        time.sleep(1)
        print(f'子進程{self.name}-->end')

if __name__ == '__main__':
    p = Test('xichen')
    p1 = Test('nick')
    p.start() # 向操做系統發送開啓子進程,這個時候會自動觸發Test類的run方法
    p1.start()
    print('主進程/子進程')

主進程/子進程
子進程xichen-->start
子進程nick-->start
子進程xichen-->end
子進程nick-->end

4.3 join 方法

  • join方法:是在子進程開啓的時候加了堵塞,就是父進程必需要等待子進程的結束,才結束父進程
def f1():
    print('子進程start')
    time.sleep(2)
    print('子進程end')

if __name__ == '__main__':
    print('開始父進程')
    p = Process(target=f1)
    p.start()

    p.join() # 是父進程要等待子進程結束後,再啓動父進程
    # time.sleep(5) 其實核心其實就是在啓動主進程的以前加一個延時,讓他等子進程結束後再執行
    print('我是父進程,等待子進程運行結束再結束')

開始父進程
子進程start
子進程end
我是父進程,等待子進程運行結束再結束

1.join方法實現多個子進程並行結束後,在結束父進程
from multiprocessing import Process
import time

def f(i):
    print(f'進程{i} start')
    time.sleep(i)
    print(f'進程{i} end')

if __name__ == '__main__':
    print('開始父進程')
    p1 = Process(target=f,args=(1,))
    p2 = Process(target=f,args=(2,))
    p3 = Process(target=f,args=(3,))
    start = time.time()
    p1.start()
    p2.start()
    p3.start()

    # join核心就是將程序堵塞在這裏,主進程須要等待子進程結束之後,在去執行
    p1.join()
    p2.join()
    p3.join()
    end = time.time()
    # 總時長:按照最長的時間計算多一點
    print('程序執行的時間:',end - start) # 3s多,由於執行的進程是在一個時間的
    print('啓動父進程')

開始父進程
進程1 start
進程2 start
進程3 start
進程1 end
進程2 end
進程3 end
程序執行的時間: 3.5759353637695312
啓動父進程

2.join方法實現多個子進程串行結束後,在結束父進程

# 用Process類來建立多個子進程,而且用join方法讓父進程等待着子進程結束之後,在結束父進程
# 可是這樣太耗費時間了,還不如直接調用咱們寫的函數實現
# 1.實現多個進程串行結束之後,在結束掉父進程
def f(i):
    print(f'子進程{i} start')
    time.sleep(i)
    print(f'子進程{i} end')


if __name__ == '__main__':
    print('開始父進程')
    p1 = Process(target=f,args=(1,))
    p2 = Process(target=f,args=(2,))
    p3 = Process(target=f,args=(3,))
    start = time.time()

    p1.start()
    p1.join()

    p2.start()
    p2.join()

    p3.start()
    p3.join()

    end = time.time()
    print('程序執行的總時長:',end-start) # 7s多,
    print('結束父進程')
    
  
# 2.直接利用函數調用的方法來實現多個子進程串行的結束
# def f1(i):
#     print(f'子進程{i} start')
#     time.sleep(i)
#     print(f'子進程{i} end')
#
# if __name__ == '__main__':
#     print('開啓主進程')
#     start = time.time()
#     f1(1)
#     f1(2)
#     f1(3)
#     end = time.time()
#     print('程序執行的總時間:',end - start) # 6秒多,
#     print('結束父進程')
開啓主進程
子進程1 start
子進程1 end
子進程2 start
子進程2 end
子進程3 start
子進程3 end
程序執行的總時間: 6.001583814620972
結束父進程

開始父進程
子進程1 start
子進程1 end
子進程2 start
子進程2 end
子進程3 start
子進程3 end
程序執行的總時長: 7.081932783126831
結束父進程

3.優化join實現多個子進程並行結束後,結束父進程
# 對join實現多個子進程並行結束在結束主進程,作了優化,
# 咱們不用在建立多個子進程的時候,每次用Process類來實例化一個新的進程對象
# 能夠直接使用循環,這樣更方便簡潔一些
def f(i):
    print(f'子進程{i} start')
    time.sleep(i)
    print(f'子進程{i} end')

if __name__ == '__main__':
    print('開始執行主進程')
    p_lst = []
    start = time.time()
    for i in range(1,4):
        p = Process(target=f,args=(i,))
        p_lst.append(p)
        p.start()
    for p in p_lst:
        p.join()

    end = time.time()
    print('結束主進程')
    print('程序執行的總時長:',end-start)

開始執行主進程
子進程2 start
子進程1 start
子進程3 start
子進程1 end
子進程2 end
子進程3 end
結束主進程
程序執行的總時長: 3.49997878074646

4.4 Process類查看進程號的方法

  • Process類查看進程號(id號)
  • pid 直接用進程對象.pid是在主進程裏查看子進程的pip號
  • os.getpid 能夠在子進程裏查看子進程的進程號、能夠在父進程裏查看父進程的進程號
  • os.getppid 能夠在子進程裏查看子進程的父進程的進程號、能夠在父進程裏查看父進程的父進程號
  • current_process().pid 查看當前進程的進程號
from multiprocessing import Process
import os,time

def f(i):
    print(f'子進程{i}  start')
    print('在子進程中查看本身的進程號pid:',os.getpid(),'在子進程中查看父進程的進程號pid:',os.getppid())
    time.sleep(i)
    print(f'子進程{i}  end')

if __name__ == '__main__':
    print('開始父進程')
    p = Process(target=f,args=(1,))
    p.start()
    p.join()

    print('在父進程中查看子進程的進程號pid:',p.pid,'在父進程中查看主進程進程號:',os.getpid()
              ,'在父進程中查看父進程的父進程號pid:',os.getppid())
    print('結束父進程')

開始父進程
子進程1 start
在子進程中查看本身的進程號pid: 15352 在子進程中查看父進程的進程號pid: 12416
子進程1 end
在父進程中查看子進程的進程號pid: 15352 在父進程中查看主進程進程號: 12416 在父進程中查看父進程的父進程號pid: 12248
結束父進程

4.5 進程對象的terminate和is_alive方法

  • terminate 是用來關閉進程的
  • is_alive 是判斷一個進程的存活狀態的,返回True/Flase
from multiprocessing import Process
import os
import time

# 1.terminate 方法
def f(i):
    print(f'子進程{i}  start')
    time.sleep(i)
    print(f'子進程{i}  end')

if __name__ == '__main__':
    print('開始執行父進程')
    p = Process(target=f,args=(1,))
    p.start()

    p.terminate() # 用來關閉打開的子進程,可是他不會立馬關閉,因此is_alive查看進程存活狀態的時候,不必定是已經死了
    print(p.is_alive())
    p.join()
    print(p.is_alive())
    print('結束父進程')

開始執行父進程
True
False
結束父進程

4.6 current_Process方法

  • current_process 方法是用來查看當前進程的進程名字,進程號
  • current_process().name
  • current_process().pid
import time
from multiprocessing import Process,current_process

def f(i):
    print(f'子進程{i}  start')
    print(f'當前進程{i}的名字爲:',current_process().name)
    print(f'當前進程{i}的進程號爲:',current_process().pid)
    print(f'子進程{i}  end')

if __name__ == '__main__':
    print('開始執行父進程')
    p1 = Process(target=f,args=(1,))
    p2 = Process(target=f,args=(2,))
    p3 = Process(target=f,args=(3,))

    p1.start()
    p1.join()

    p2.start()
    p2.join()

    p3.start()
    p3.join()

    print('結束父進程')

開始執行父進程
子進程1 start
當前進程1的名字爲: Process-1
當前進程1的進程號爲: 6232
子進程1 end
子進程2 start
當前進程2的名字爲: Process-2
當前進程2的進程號爲: 14552
子進程2 end
子進程3 start
當前進程3的名字爲: Process-3
當前進程3的進程號爲: 12896
子進程3 end
結束父進程

4.7 進程的name屬性

  • 利用繼承Process類來建立進程的時候,涉及了進程對象的屬性問題
from multiprocessing import Process
class Mydata(Process):
    def __init__(self,name):
        # self.name = name  # 這裏的name屬性是Process類的name屬性,表示的是當前進程的名字,打印進程的名字的時候
        # 打印的並非咱們須要的實例化進程時給的初始值
        開始執行父進程
        子進程Mydata-1 start
        子進程Mydata-1 start
        結束父進程
        super().__init__() # 在這裏執行父類Process的__init__方法的時候,process裏的init方法由會將name屬性的值給覆蓋掉
        self.name = name # 若是不想要覆蓋掉咱們的name屬性值的話,咱們須要在super()後面對進程對象的name屬性值賦值

    def run(self):
        print(f'子進程{self.name} start')
        print(f'子進程{self.name} start')

if __name__ == '__main__':
    print('開始執行父進程')
    p = Mydata('xichen')
    p.start()
    p.join()
    print('結束父進程')

開始執行父進程
子進程xichen start
子進程xichen start
結束父進程

5、守護進程

  • 守護進程就是伴隨某一個子進程
  • 本質也是一個子進程
  • 主進程的代碼執行完畢守護進程直接結束。
  • 可是此時主進程可能沒有結束.
  • 定義守護進程:進程對象.daemon = True 就是把這個進程定義爲守護進程
  • p.daemon=True必定要在p.start()前設置,設置p爲守護進程,禁止p建立子進程,而且父進程代碼執行結束,p即終止運行
from multiprocessing import Process
import time
def foo(i):
    print(f'守護進程{i} start')
    time.sleep(2)
    print(f'守護進程{i} end')


def fo(i):
    print(f'子進進程{i} start')
    time.sleep(2)
    print(f'子進進程{i} end')

if __name__ == '__main__':
    print('開始執行父進程')
    p = Process(target=foo,args=(1,))
    p1 = Process(target=fo,args=(2,))
    p.daemon = True # 把這個子進程定義爲了守護進程
    # 它不會影響p2這個進程對象的執行
    p.start()
    p1.start()
    time.sleep(1)
    print('父進程結束')

開始執行父進程
子進進程2 start
守護進程1 start
父進程結束
子進進程2 end

6、使用多進程實現socket聊天併發

6.1使用多進程實現socket聊天併發——server

# Author:xichen
# @Time:2019/9/1114:24
import socket
from multiprocessing import Process

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('192.168.11.78',8002))
server.listen(10)

def Mydata(conn,client_addr):
    while True:
        try:

            data = conn.recv(1024)
            if not data:
                conn.close()
                break
            print('子進程的數據爲:',data)
            conn.send(data.upper())
            print('向子進程發送的數據爲:',data.upper().decode('utf8'))
        except Exception:
            break

if __name__ == '__main__':
    while True:
        conn,client_addr = server.accept()
        print(f'開啓子進程:地址爲:{client_addr}')
        p = Process(target = Mydata,args=(conn,client_addr))
        p.start()

6.2使用多進程實現socket聊天併發——client

import socket

client = socket.socket()
client.connect(('192.168.11.78',8002))

while True:
    data = input('開始發送數據:')
    if not data:
        break
    client.send(data.encode('utf8'))
    msg = client.recv(1024)
    print('接收服務端返回

7、進程之間的數據隔離問題

進程之間的數據隔離問題

rom multiprocessing import Process
import time

x = 0
def task():
    global x
    x = 100
    print('子進程的x修改成了{}'.format(x))

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    time.sleep(5)
    print(x)

子進程的x修改成了100

0

相關文章
相關標籤/搜索