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()
相关文章
相关标签/搜索