Python多進程編程及多進程間的通訊,數據傳輸

 

多進程編程及進程間的通訊

  • 意義:充分利用計算機的資源提升程序的運算速率
  • 定義:經過應用程序利用計算機多個核心達到同時執行多個任務的目的,以此提升計算機的運行速率
  • 實施方案:多進程 多線程
  • 並行: 計算機同時處理多個任務
  • 併發:同時處理多個任務,內核在不斷的任務間小虎切換,達到好像還都在處理運行的效果,可是實際是一個時間點內核只能處理其中一個任務

多進程的優缺點

  • 優勢node

    • 可使用計算機多核,進行任務的併發執行,提升執行效率
    • 空間獨立,數據安全
    • 運行不受其餘進程影響,建立方便
  • 缺點python

    • 進程的刪除和建立消耗的系統資源多

進程(process)

查看進程:ps -aux 查看進程樹:pstree 查看父進程:ps -ajxgit

標誌 名稱 說明
S 等待態 可中斷等待
D 等待態 不可中斷等待
T 等待態 暫停狀態
R 運行態 包含就緒狀態
Z 殭屍進程  
<   高優先級
N   優先級較低
l   有子進程的
s   會話組組長
+   前臺進程

三態

  • 就緒態:進程具有執行條件,等待系統分配資源
  • 運行態:進程佔有CPU,處於運行狀態
  • 等待態:進程暫時不具有執行條件,阻塞等待,知足條件後再執行

五態(三態的基礎上增長了新建態和終止態)

  • 新建態:建立一個新的進程,獲取資源的過程
  • 終止態:進程執行結束,資源釋放回收的過程

進程優先級

  • 做用:決定了一個進程的執行權限github

  • 動態查看系統中的進程信息:top,用< , >翻頁web

    • 取值範圍:-20 -- 19 -20優先級最高
  • 使用指定的優先級運行程序編程

    • nice : 指定運行的優先級windows

      e.g. :nice -9 ./while.py --->>以優先級-9運行安全

進程特徵

  1. 進程之間運行互不影響,各自獨立運行
  2. 進程是操做系統資源分配的最小單位
  3. 每一個進程空間獨立,各自佔有必定的虛擬內存

孤兒進程

  1. 父進程先於子進程退出,此時子進程就稱爲孤兒進程
  2. 孤兒進程會被操做系統指定的進程收養,系統進程就成爲了孤兒進程的父進程

殭屍進程

  1. 子進程先於父進程退出,可是父進程沒有處理子進程的退出狀態,此時子進程就會成爲殭屍進程
  2. 殭屍進程會存留少許的PCB信息在內存中,大量的殭屍進程會消耗系統資源,應該避免殭屍進程的產生
  • 如何避免殭屍進程的產生數據結構

    • 處理子進程的退出狀態

      pid, status = os.wait()

       

      功能:在父進程中堵塞等待子進程退出

      返回值:

要求理解

  1. 什麼是進程?
  2. 瞭解進程特徵
  3. 清楚進程每種狀態,

多進程編程

 
 1  
 2   
 3 # 功能 : 建立新的進程
 4 # 參數 : 無
 5 # 返回值 : 失敗--->返回一個負數
 6 #  成功--->在新的進程中返回 ‘0’
 7 # 在原有的進程中返回新的進程的PID號
 8 import os
 9 pid = os.fork()
10 pid = os.fork()
11 print(pid)
12 13 '''打印
14 9352
15 0
16 9353
17 '''

 

  • 子進程會複製父進程的所有代碼,包括fork以前產生的內存空間
  • 子進程從fork的下一句開始執行,與父進程互不干擾
  • 父子進程的執行順序不必定,父子進程公用一個終端顯示
  • 父子進程一般會根據fork返回值的差別選擇執行不一樣的代碼。因此if結構幾乎是fork的標配
  • 父子進程空間獨立,操做都是本空間的內容,互不影響
  • 子進程也有本身的特性,好比PID號,PCB,命令集等。

進程相關的函數

函數方法 參數 說明
os.getpid()   返回當前進程的PID號
os.getppid()   返回當前進程的父進程的PID號
os._exit( status ) 程序的退出狀態 進程退出
sys.exit( [ status ] ) 數字:表示退出狀態,不寫默認爲 進程退出
  
import os
pid = os.fork()
if pid < 0:
    print("建立進程失敗")
elif pid == 0:
    print("子進程個人真實PID爲:",os.getpid(),"個人父進程PID爲:",os.getppid())
else:
    print("我是父進程執行的代碼,當前的變量pid爲:",pid,"個人真實PID爲:",os.getpid())
    
'''打印內容''''''
我是父進程執行的代碼,當前的變量pid爲: 10992 個人真實PID爲: 10991
子進程個人真實PID爲: 10992 個人父進程PID爲: 10991
''''''打印內容'''
    
# 若是pid 10992和子進程真實PID不一樣,那麼這個子進程就變成了孤兒進程

 

多進程模塊

import multiprocessing

from multiprocessing import Process

Process()

  • 功能:建立一個進程對象

  • 參數

    • name:進程名稱
    • target:綁定函數
    • args:元組,給target函數按照位置傳參
    • kwargs:字典,給target函數按照鍵值對傳參
    • name: 字符串,新的進程的名字
    • 例如:p = Process(target = fun,args=(a,b))
函數方法 說明
p.start() 啓動進程,target函數自動執行,此時進程被真正建立
p.join([timeout]) 阻塞等待回收子進程,timeout爲超時時間
p.is_alive() 判斷進程生命週期狀態,處於生命週期,返回布爾值
p.name() 獲取進程名稱
p.pid() 獲取進程 的pid
p.daemon() 默認狀態False,主進程退出不影響子進程。True :子進程隨着主進程結束
  • 使用multiprocessing建立子進程,一樣子進程複製父進程的所有代碼,父子進程各自執行互不影響,父子進程有各自的運行空間

  • 若是不使用join揮手子進程則子進程退出後會成爲殭屍進程

from multiprocessing import Process 
from time import sleep 
​
#帶參數的進程函數
def worker(sec,name):
    for i in range(3):
        sleep(sec)
        print("I'm %s"%name)
        print("I'm working...")
​
p = Process(target = worker,args = (2,),\
    kwargs = {'name':'Daivl'},name = "Worker")
p.start()
​
print("Process name:",p.name) #進程名稱
print("Process PID:",p.pid) #獲取進程PID
#進程alive狀況
print("Process is alive:",p.is_alive())
​
p.join(3)
print("==================")

 

 

 

建立自定義繼承Process類

  1. 繼承Process

  2. 編寫本身的__init__,同時加載父類的__init__方法

  3. 重寫run方法,能夠經過生成的對象調用start自動運行

from  import Process
import time
​
class ClockProcess(Process):
​
    def __init__(self,value):
        self.value = value
        super(ClockProcess,self).__init__()
​
    def run(self):
        for i in range(5):
            print("如今的時間是",time.ctime())
            time.sleep(self.value)
​
# 建立自定義進的類的對象
p =ClockProcess(2)
​
# 自動調用run
p.start()
p.join()    

 

進程池技術

  • 產生緣由

    • 若是有大量任務須要多進程完成。則須要頻繁的建立刪除進程,給計算機帶來較多的資源消耗
  • 原理

    • 建立適當的進程放入進程池,用來池裏待處理的時間,處理完當前任務後進程不銷燬,仍然哎進程池等待處理其餘時間,進程的複用下降了系統資源的消耗
  • 使用方法

    1. 建立進程池,在池內放入適當的進程
    2. 將事件加入到事件等待隊列
    3. 不斷取進程執行時間,直到全部進程執行完畢
    4. 關閉進程池,回收進程

Pool函數

  • Pool(Processes)

    • 功能:建立進程池對象
    • 表示進程池中有多少進程
  • pool.apply_async(func, args, kwds)

    • 功能:將時間放入進程池隊列

    • 參數

      • func:事件函數
      • args:以元組形式給func傳參
      • kwds : 以字典形式給func傳參
    • 返回值一個對象

  • pool.apply(func, args, kwds)

    • 功能:將時間放入進程池隊列

    • 參數

      • func:事件函數
      • args:以元組形式給func傳參
      • kwds : 以字典形式給func傳參
  • pool.close()

    • 功能:關閉進程池
  • pool.join()

    • 功能:回收進程池
  • pool.map(func,iter)

    • 功能:將要作的事件放入進程池

    • 參數

      • func : 要執行的函數
      • iter : 迭代對象
    • 返回值:屢次return的結果列

from multiprocessing import Process
import time
​
class ClockProcess(Process):
​
    def __init__(self,value):
        self.value = value
        super(ClockProcess,self).__init__()
​
    def run(self):
        for i in range(5):
            print("如今的時間是",time.ctime())
            time.sleep(self.value)
​
# 建立自定義進的類的對象
p =ClockProcess(2)
​
# 自動調用run
p.start()
p.join()    

 

進程間的通信(IPC)

進程間通信的方法有:管道,消息隊列,共享內存,信號,信號量,套接字

  管道 消息隊列 共享內存
開闢空間 內存 內存 內存
讀寫方式 兩端讀寫[雙向/單向] 先進先出 覆蓋以前的內容
效率 通常 通常 較高
應用 多用於父子進程 普遍靈活 須要注意互斥

 

管道通信

通訊原理:在內存中開闢管道空間,生成管道操做對象,多個進程使用「同一個」管道對象進行操做,便可實現通訊

multiprocessing --> Pipe

  • fd1,fd2 = Pipe(duplex = True)

    • 功能:建立管道

    • 參數

      • 默認表示爲雙向管道
      • 設置False則爲單向管道
    • 返回值

      • 若是是雙向管道則均可以讀寫
      • 若是是單向管道,則fd1只讀,fd2只寫
  • fd.recv()

    • 功能:從管道讀取信息
    • 返回值:讀取到的內容
    • 若是管道爲空則堵塞
  • fd.send(data)

    • 功能:向管道中寫入內容
    • 參數:要寫入的內容
    • 能夠發送任意Python數據類型

多進程管道傳輸數據示例 

from multiprocessing import Process,Pipe
import time,os
​
# 建立管道對象
fd1, fd2 = Pipe()
​
def fun(name):
    time.sleep(1)
    fd2.send(os.getppid())
​
jobs = []
# 建立5個子進程
for i in range(5):
    p = Process(target = fun,args = (i,))
    jobs.append(p)
    p.start()
​
for i in range(5):
    data = fd1.recv()
    print(data)
​
for i in jobs:
    i.join()

 

 

消息隊列

隊列:先進先出,按照順序來

通訊原理:在內存中創建隊列數據結構模型。多個進程均可以經過隊列存入內容,取出內容的順序和存入的順序保持一致

  • 建立隊列

    q = Queue(maxsize = 0)

    • 功能:建立隊列消息
    • 參數:表示最多存儲多少消息。默認表示根據內存分配存儲
    • 返回值:隊列對象
  • q.put(data, [block, timeout])

    • 功能:像隊列存儲消息

    • 參數

      • data:要存的內容
      • block:默認隊列滿時候會堵塞,設置False則非堵塞
      • timeout:超時時間
  • data = q.get([block, timeout])

    • 功能:獲取隊列消息

    • 參數

      • block:默認設置隊列空時會堵塞,設置爲False則非堵塞
      • timeout:超時時間
    • 返回值:返回取出的內容

  • q.full():判斷隊列是否爲滿

  • q.empty():判斷隊列是否爲空

  • q.size():判斷隊列中的消息數量

  • q.close:關閉隊列

單進程示例

#!《 單進程 》
from multiprocessing import Queue
from time import sleep
​
# 建立隊列,能夠放3條消息
q = Queue(3)
# 存一條消息
q.put(1)
sleep(0.5)
# 判斷隊列是否爲空
print(q.empty())
q.put(2)
# 判斷隊列是否滿
print(q.full())
q.put(3)
# 輸出隊列消息數量
print(q.qsize())
# 輸出
print(q.get())
q.close()

 

 

多進程消息隊列傳遞數據

from multiprocessing import Queue,Process
from time import sleep
​
# 建立隊列,能夠放3條消息
q = Queue(3)
​
def fun1():
    sleep(1)
    q.put({"a":1,"b":2})
​
def fun2():
    sleep(2)
    print("收到消息",q.get())
​
p1 = Process(target = fun1)
p2 = Process(target = fun2)
p1.start()
p2.start()
p1.join()
p2.join()

 

 

 

共享內存

通訊原理:在內存中開闢一段空間,對多個進程可見,進程能夠寫入能夠讀,可是每次寫入的內容會覆蓋上一次的內容。

只能進行單個數據的發送

  • obj = Value(ctype, obj)

    • 功能:開闢共享內存空間

    • 參數

      • ctype:要存儲的數據類型
      • obj:共享內存的初始數據
    • 返回值:共享內存對象

    • obj.value即共享內存值,對其修改便可修改內存

    • from multiprocessing import Value
      from time import sleep
      import os
      # 建立共享內存對象
      money = Value('i',2000)
      ​
      # 操做共享內存
      def deposite():
          while True:
              i = int(input("請輸入:"))
              money.value = i
              sleep(0.05)
      ​
      def withdraw():
          data = money.value
          while True:
              if data != money.value :
                  data = money.value
                  print(data)
                  
      pid = os.fork()
      if pid == 0 :
          deposite()
      withdraw() 
  • obj = Array(ctype, obj)

    • 功能:開闢共享內存空間

    • 參數:

      • ctype:要存入的數據格式
      • obj:初始化存入的內容,好比列表、字符串。若是是數字則表明開闢內存空間的個數
    • 返回值:返回共享內存對象,能夠遍歷 

    • from multiprocessing import Array,Process
      from time import sleep
      import os
      ​
      # 開闢100字符內存空間,'c'表明字符,'i'表明整形
      shm = Array('c',100)
      # 必須使用字節流
      shm.value = "哈哈哈".encode()
      def fun1():
          print(os.getpid(),"子進程1:",shm.value.decode())
          shm.value = "夜夜夜".encode()
      ​
      def fun2():
          sleep(1)
          print(os.getpid(), "子進程2:",shm.value.decode())
      ​
      p1 = Process(target = fun1)  
      p2 = Process(target = fun2)
      p1.start()
      p2.start()
      p1.join()
      p2.join()
      ​
      ​

       

信號通訊

一個進程向另外一個進程發送一個信號來傳遞某種信息,接受者根據傳遞的信息來作相應的事

$ kill -l查看系統信號說明

$ kill -9 pid號對進程發送信號

信號名稱 說明    
1) SIGHUP 鏈接斷開    
2) SIGINT ctrl+c    
3) SIGQUIT ctrl+\    
20) SIGTSTP ctrl+z    
9) SIGKILL 終止進程    
19) SIGSTOP 暫停進程    
26) SIGVTALRM 時鐘信號    
17) SIGCHLD 子進程退出時給父進程發的信號    
       

在Python中import signal能夠獲取信號

  • os.kill(pid, sig)

    • 功能:發送信號

    • 參數

      • pid:要發送信號的PID號
      • sig :信號名稱 
import os
import signal
os.kill(12345,signal.SIGKILL) #殺死進程
做者: Banl
----------------------------------------------------------------------

個性簽名:苟日新 日日新 又日新!

若是這篇文章對你有些幫助,記得在右下角點個「推薦」,拜謝!

相關文章
相關標籤/搜索