python網絡進程

多任務編程

意義:充分利用計算機的資源提升程序的運行效率python

定義:經過應用程序利用計算機多個核心,達到同時執行多個任務的目的linux

實施方案多進程多線程
編程

並行:多個計算機核心並行的同時處理多個任務多線程

併發:內核在多個任務間不斷切換,達到好像內核在同時處理多個任務的運行效果併發

進程:程序在計算機中運行一次的過程app

程序:是一個可執行文件,是靜態的,佔有磁盤,不佔有計算機運行資源dom

進程:進程是一個動態的過程描述,佔有CPU內存等計算機資源的,有必定的生命週期async

* 同一個程序的不一樣執行過程是不一樣的進程,由於分配的計算機資源等均不一樣函數

父子進程 : 系統中每個進程(除了系統初始化進程)都有惟一的父進程,能夠有0個或多個子進
程。父子進程關係便於進程管理。ui

進程

CPU時間片:若是一個進程在某個時間點被計算機分配了內核,咱們稱爲該進程在CPU時間片上。

PCB(進程控制塊):存放進程消息的空間

進程ID(PID):進程在操做系統中的惟一編號,由系統自動分配

進程信息包括:進程PID,進程佔有的內存位置,建立時間,建立用戶. . . . . . . .  

進程特徵:

  1. 進程是操做系統分配計算機資源的最小單位
  2. 每個進程都有本身單獨的虛擬內存空間
  3. 進程間的執行相互獨立,互不影響

進程的狀態

一、三態

  • 就緒態:進程具有執行條件,等待系統分配CPU
  • 運行態:進程佔有CPU處理器,處於運行狀態
  • 等待態:進程暫時不具有運行條件,須要阻塞等待,讓出CPU

二、五態(增長新建態和終止態)

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

查看進程樹:   pstree

查看父進程PID:  ps -ajx

linux查看進程命令:  ps -aux

有一列爲STAT爲進程的狀態

  D 等待態 (不可中斷等待)(阻塞
  S 等待態 (可中斷等待)(睡眠
  T 等待態 (暫停狀態)
  R 運行態 (就緒態運行態)
  Z 殭屍態

  + 前臺進程(能在終端顯示出現象的)
  < 高優先級
  N 低優先級
  l 有多線程的
  s 會話組組長

 

os.fork建立進程

pid = os.fork() 

功能:建立一個子進程

返回值:建立成功在原有的進程中返回子進程的PID,在子進程中返回0;建立失敗返回一個負數

父子進程一般會根據fork返回值的差別選擇執行不一樣的代碼(使用if結構)

import  os 
from time import sleep

pid = os.fork()

if pid < 0:
    print("建立進程失敗")

#子進程執行部分
elif pid == 0:
    print("新進程建立成功")

#父進程執行部分
else:
    sleep(1)
    print("原來的進程")

print("程序執行完畢")

# 新進程建立成功
# 原來的進程
# 程序執行完畢
  • 子進程會複製父進程所有代碼段(包括fork前的代碼)可是子進程僅從fork的下一句開始執行
  • 父進程不必定先執行(進程之間相互獨立,互不影響)
  • 父子進程各有本身的屬性特徵,好比:PID號PCB內存空間
  • 父進程fork以前開闢的空間子進程一樣擁有,可是進程之間相互獨立,互不影響.

父子進程的變量域

import os 
from time import sleep 

a = 1
pid = os.fork()
if pid < 0:
    print("建立進程失敗")
elif pid == 0:
    print("子進程")
    print("a = ",a)
    a = 10000
    print("a = ",a)
else:
    sleep(1)
    print("父進程")
    print("parent a :",a)    # a = 1

# 子進程
# a =  1
# a =  10000
# 父進程
# parent a : 1

進程ID和退出函數

os.getpid() 獲取當前進程的PID號

  返回值:返回PID號

os.getppid() 獲取父類進程的進程號

  返回值:返回PID號

import os

pid = os.fork()

if pid < 0:
  print("Error")
elif pid == 0:
  print("Child PID:", os.getpid())       # 26537
  print("Get parent PID:", os.getppid()) # 26536
else:
  print("Get child PID:", pid)           # 26537
  print("Parent PID:", os.getpid())      # 26536

os._exit(status)  退出進程

  參數:進程的退出狀態  整數

sys.exit([status])  退出進程

  參數:默認爲0  整數則表示退出狀態;符串則表示退出時打印內容

  sys.exit([status])能夠經過捕獲SystemExit異常阻止退出

import os,sys

# os._exit(0)                 # 退出進程
try:
    sys.exit("退出")
except SystemExit as e:
    print("退出緣由:",e)    # 退出緣由: 退出

孤兒和殭屍

孤兒進程

父進程先於子進程退出,此時子進程就會變成孤兒進程

  孤兒進程會被系統指定的進程收養,即系統進程會成爲該孤兒進程新的父進程。孤兒進程退出時該父進程會處理退出狀態

殭屍進程

子進程先與父進程退出,父進程沒有處理子進程退出狀態,此時子進程成爲殭屍進程

  殭屍進程已經結束,可是會滯留部分PCB信息在內存,大量的殭屍會消耗系統資源,應該儘可能避免

如何避免殭屍進程的產生

父進程處理子進程退出狀態

pid, status = os.wait()

功能:在父進程中阻塞等待處理子進程的退出

返回值: pid     退出的子進程的PID號

     status   子進程的退出狀態

import os, sys

pid = os.fork()

if pid < 0:
  print("Error")
elif pid == 0:
  print("Child process", os.getpid())   # Child process 27248
  sys.exit(1)
else:
  pid, status = os.wait()     # 阻塞等待子進程退出
  print("pid : ", pid)        # pid :  27248
  # 還原退出狀態
  print("status:", os.WEXITSTATUS(status))      # status: 1
  while True:
    pass

建立二級子進程

  1. 父進程建立子進程等待子進程退出
  2. 子進程建立二級子進程,而後立刻退出
  3. 二級子進程成爲孤兒,處理具體事件
import os
from time import sleep

def fun1():
    sleep(3)
    print("第一件事情")

def fun2():
    sleep(4)
    print("第二件事情")

pid = os.fork()

if pid < 0:
    print("Create process error")
elif pid == 0:          # 子進程
    pid0 = os.fork()    # 建立二級進程
    if pid0 < 0:
        print("建立二級進程失敗")
    elif pid0 == 0:     # 二級子進程
        fun2()          # 作第二件事
    else:               # 二級進程
        os._exit(0)     # 二級進程退出
else:
    os.wait()
    fun1()              # 作第一件事

# 第一件事情
# 第二件事情

經過信號處理子進程退出

原理: 子進程退出時會發送信號給父進程,若是父進程忽略子進程信號, 則系統就會自動處理子進程退出。

方法: 使用signal模塊在父進程建立子進程前寫以下語句 :
import signal
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
特色 : 非阻塞,不會影響父進程運行。能夠處理全部子進程退出

Multiprocessing建立進程

步驟:

  1. 須要將要作的事情封裝成函數
  2. multiprocessing.Process建立進程,並綁定函數
  3. start啓動進程
  4. join回收進程 

p = multiprocessing.Process(target, [name], [args], [kwargs]) 

建立進程對象
參數:

  • target : 要綁定的函數名
  • name : 給進程起的名稱 (默認Process-1)
  • args: 元組 用來給target函數傳參
  • kwargs : 字典 用來給target函數鍵值傳參

p.start()
功能 : 啓動進程 自動運行terget綁定函數。此時進程被建立

p.join([timeout])
功能: 阻塞等待子進程退出,最後回收進程
參數: 超時時間

multiprocessing的注意事項:

    • 使用multiprocessing建立進程子進程一樣複製父進程的所有內存空間,以後有本身獨立的空間,執行上互不干擾
    • 若是不使用join回收可能會產生殭屍進程
    • 通常父進程功能就是建立子進程回收子進程,全部事件交給子進程完成
    • multiprocessing建立的子進程沒法使用ptint
import multiprocessing as mp 
from time import sleep 
import os

a = 1

def fun():
    sleep(2)
    print("子進程事件",os.getpid())
    global a
    a = 10000
    print("a = ",a)

p = mp.Process(target = fun)    # 建立進程對象
p.start()   # 啓動進程
sleep(3)
print("這是父進程")
p.join()    # 回收進程
print("parent a:",a)

# 子進程事件 5434
# a =  10000
# 這是父進程
# parent a: 1

Process(target)

multiprocessing進程屬性

p.name       進程名稱

p.pid     對應子進程的PID號

p.is_alive()    查看子進程是否在生命週期

p.daemon         設置父子進程的退出關係

  若是等於True則子進程會隨父進程的退出而結束,就不用使用 join(),必需要求在start()前設置

進程池

引言:若是有大量的任務須要多進程完成,而任務週期又比較短且須要頻繁建立。此時可能產生大量進程頻繁建立銷燬的狀況,消耗計算機資源較大,這個時候就須要進程池技術

進程池的原理:建立必定數量的進程來處理事件,事件處理完進程不退出而是繼續處理其餘事件,直到全部事件全都處理完畢統一銷燬。增長進程的重複利用,下降資源消耗。 

1.建立進程池,在池內放入適當數量的進程

from multiprocessing import Pool

Pool(processes)  建立進程池對象

  • 參數:進程數量
  • 返回 : 指定進程數量,默認根據系統自動斷定 

2.將事件封裝函數,放入到進程池

pool.apply_async(fun,args,kwds)  將事件放入進程池執行
參數:

  • fun 要執行的事件函數
  • args 以元組爲fun傳參
  • kwds 以字典爲fun傳參

返回值 : 

  • 返回一個事件對象 經過get()屬性函數能夠獲取fun的返回值 

3.關閉進程池

 pool.close()  關閉進程池,沒法再加入事件

4.回收進程

pool.join()  回收進程池 

from multiprocessing import Pool
from time import sleep,ctime

pool = Pool(4)    # 建立進程池
# 進程池事件
def worker(msg):
  sleep(2)
  print(msg)
  return ctime()

# 向進程池添加執行事件
for i in range(4):
  msg = "Hello %d"%i

  # r 表明func事件的一個對象
  r = pool.apply_async(func=worker,args=(msg,))

pool.close()    # 關閉進程池
pool.join()     # 回收進程池

# Hello 3
# Hello 2
# Hello 0
# Hello 1

進程間通訊(IPC)

因爲進程間空間獨立,資源沒法共享,此時在進程間通訊就須要專門的通訊方法。

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

管道通訊(Pipe)

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

from multiprocessing import Pipe

fd1, fd2 = Pipe(duplex = True)

  • 功能:建立管道
  • 參數:默認表示雙向管道,若是爲False 表示單向管道
  • 返回值:表示管道兩端的讀寫對象;若是是雙向管道都可讀寫;若是是單向管道fd1只讀 fd2只寫

fd.recv()

  • 功能 : 從管道獲取內容
  • 返回值:獲取到的數據,當管道爲空則阻塞

fd.send(data)

  • 功能: 向管道寫入內容
  • 參數: 要寫入的數據 

注意:

  1. multiprocessing中管道通訊只能用於父子關係進程中 
  2. 管道對象在父進程中建立,子進程經過父進程獲取 
from multiprocessing import Pipe, Process

fd1, fd2 = Pipe()   # 建立管道,默認雙向管道
def fun1():
  data = fd1.recv()     # 從管道獲取消息
  print("管道2傳給管道1的數據", data)
  inpu = "跟你說句悄悄話"
  fd1.send(inpu)

def fun2():
  fd2.send("肥水不流外人天")
  data = fd2.recv()
  print("管道1傳給管道2的數據", data)

p1 = Process(target=fun1)
P2 = Process(target=fun2)

p1.start()
P2.start()

p1.join()
P2.join()
# 管道2傳給管道1的數據 肥水不流外人天
# 管道1傳給管道2的數據 跟你說句悄悄話

消息隊列

從內存中開闢隊列結構空間,多個進程能夠向隊列投放消息,在取出來的時候按照先進先出順序取出 

q = Queue(maxsize = 0)  

建立隊列對象

  • maxsize :默認表示系統自動分配隊列空間;若是傳入正整數則表示最多存放多少條消息
  • 返回值 : 隊列對象

q.put(data,[block,timeout])  

向隊列中存入消息

  • data:存放消息(python數據類型)
  • block:默認爲True表示當前隊列滿的時候阻塞,設置爲False則表示非阻塞
  • timeout:blockTrue表示超時時間

  返回值:返回獲取的消息

q.get([block,timeout])

從隊列取出消息

  • 參數:block 設置是否阻塞 False爲非阻塞;timeout 超時檢測
  • 返回值: 返回獲取到的內容

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

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

q.qsize()  判斷當前隊列有多少消息 

q.close()  關閉隊列

from multiprocessing import Process, Queue
from time import sleep
from random import randint

#  建立消息隊列
q = Queue(3)


# 請求進程
def request():
  for i in range(2):
    x = randint(0, 100)
    y = randint(0, 100)
    q.put((x, y))


# 處理進程
def handle():
  while True:
    sleep(1)
    try:
      x, y = q.get(timeout=2)
    except:
      break
    else:
      print("%d + %d = %d" % (x, y, x + y))


p1 = Process(target=request)
p2 = Process(target=handle)
p1.start()
p2.start()
p1.join()
p2.join()
# 12 + 61 = 73
# 69 + 48 = 117

共享內存 

在內存中開闢一段空間,存儲數據,對多個進程可見,每次寫入共享內存中的數據會覆蓋以前的內容,效率高,速度快

from multiprocessing import Value, Array

obj = Value(ctype,obj)

功能:開闢共享內存空間 

參數:ctype  字符串  要轉變的c的數據類型,對比類型對照表

  obj  共享內存的初始化數據

返回:共享內存對象

from multiprocessing import Process,Value
import time
from random import randint

# 建立共享內存
money = Value('i', 5000)

#  修改共享內存
def man():
  for i in range(30):
    time.sleep(0.2)
    money.value += randint(1, 1000)

def girl():
  for i in range(30):
    time.sleep(0.15)
    money.value -= randint(100, 800)

m = Process(target=man)
g = Process(target=girl)
m.start()
g.start()
m.join()
g.join()

print("一月餘額:", money.value)   # 獲取共享內存值
# 一月餘額: 4264

obj = Array(ctype,obj)

功能:開闢共享內存

參數:ctype  要轉化的c的類型

  obj  要存入共享的數據

    若是是列表  將列表存入共享內存,要求數據類型一致

    若是是正整數  表示開闢幾個數據空間

from multiprocessing import Process, Array

# 建立共享內存
# shm = Array('i',[1,2,3])
# shm = Array('i',3)  # 表示開闢三個空間的列表
shm = Array('c',b"hello") #字節串

def fun():
  # 共享內存對象可迭代
  for i in shm:
    print(i)
  shm[0] = b'H'

p = Process(target=fun)
p.start()
p.join()

for i in shm:   # 子進程修改,父進程中也跟着修改
  print(i)

print(shm.value) # 打印字節串 b'Hello'

信號量(信號燈集)

通訊原理:給定一個數量對多個進程可見。多個進程均可以操做該數量增減,並根據數量值決定本身的行爲。

from multiprocessing import Semaphore
sem = Semaphore(num)
建立信號量對象

  • 參數 : 信號量的初始值
  • 返回值 : 信號量對象

sem.acquire() 將信號量減1 當信號量爲0時阻塞
sem.release() 將信號量加1
sem.get_value() 獲取信號量數量

from multiprocessing import Process, Semaphore

sem = Semaphore(3)    # 建立信號量,最多容許3個任務同時執行

def rnewu():
  sem.acquire()   # 每執行一次減小一個信號量
  print("執行任務.....執行完成")
  sem.release()   # 執行完成後增長信號量


for i in range(3):  # 有3我的想要執行任務
  p = Process(target=rnewu)
  p.start()
  p.join()
相關文章
相關標籤/搜索