【1】 父进程创建子进程,等待回收子进程
【2】 子进程创建二级子进程然后退出
【3】 二级子进程称为孤儿,和原来父进程一同执行事件
3>通过信号处理子进程退出
原理: 子进程退出时会发送信号给父进程,如果父进程忽略子进程信号,则系统就会自动处理子进程退出。
方法: 使用signal模块在父进程创建子进程前写如下语句 :
import signal
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
特点 : 非阻塞,不会影响父进程运行。可以处理所有子进程退出
multiprocessing 模块创建进程
进程创建方法
1. 流程特点
【1】 将需要子进程执行的事件封装为函数
【2】 通过模块的Process类创建进程对象,关联函数
【3】 可以通过进程对象设置进程信息及属性
【4】 通过进程对象调用start启动进程
【5】 通过进程对象调用join回收进程
2. 基本接口使用
Process()
功能 : 创建进程对象
参数 : target 绑定要执行的目标函数
args 元组,用于给target函数位置传参
kwargs 字典,给target函数键值传参
p.start()
功能 : 启动进程
注意:启动进程此时target绑定函数开始执行,该函数作为子进程执行内容,此时进程真正被创建
p.join([timeout])
功能:阻塞等待回收进程
参数:超时时间
注意:
1>使用multiprocessing创建进程同样是子进程复制父进程空间代码段,父子进程运行互不影响。
2>子进程只运行target绑定的函数部分,其余内容均是父进程执行内容。
3>multiprocessing中父进程往往只用来创建子进程回收子进程,具体事件由子进程完成。
4>multiprocessing创建的子进程中无法使用标准输入
3. 进程对象属性
p.name 进程名称
p.pid 对应子进程的PID号
p.is_alive() 查看子进程是否在生命周期
p.daemon 设置父子进程的退出关系
1>如果设置为True则子进程会随父进程的退出而结束
2>要求必须在start()前设置
3>如果daemon设置成True 通常就不会使用 join()
进程池实现
1. 必要性
【1】 进程的创建和销毁过程消耗的资源较多
【2】 当任务量众多,每个任务在很短时间内完成时,需要频繁的创建和销毁进程。此时对计算机压力较大
【3】 进程池技术很好的解决了以上问题。
2. 原理
创建一定数量的进程来处理事件,事件处理完进 程不退出而是继续处理其他事件,直到所有事件全都处理完毕统一销毁。增加进程的重复利用,降低资源消耗。
3. 进程池实现
【1】 创建进程池对象,放入适当的进程
from multiprocessing import Pool
Pool(processes)
功能: 创建进程池对象
参数: 指定进程数量,默认根据系统自动判定
【2】 将事件加入进程池队列执行
pool.apply_async(func,args,kwds)
功能: 使用进程池执行 func事件
参数: func 事件函数
args 元组 给func按位置传参
kwds 字典 给func按照键值传参
返回值: 返回函数事件对象
【3】 关闭进程池
pool.close()
功能: 关闭进程池
【4】 回收进程池中进程
pool.join()
功能: 回收进程池中进程
进程间通信(IPC)
1. 必要性: 进程间空间独立,资源不共享,此时在需要进程间数据传输时就需要特定的手段进行数据通信。
2. 常用进程间通信方法
管道 消息队列 共享内存 信号 信号量 套接字
1>管道通信(Pipe)
1. 通信原理:在内存中开辟管道空间,生成管道操作对象,多个进程使用同一个管道对象进行读写即可实现通信
2. 实现方法
from multiprocessing import Pipe
fd1,fd2 = Pipe(duplex = True)
功能: 创建管道
参数:默认表示双向管道
如果为False 表示单向管道
返回值:表示管道两端的读写对象
如果是双向管道均可读写
如果是单向管道fd1只读 fd2只写
fd.recv()
功能 : 从管道获取内容
返回值:获取到的数据
fd.send(data)
功能: 向管道写入内容
参数: 要写入的数据
2>消息队列
1.通信原理:在内存中建立队列模型,进程通过队列将消息存入,或者从队列取出完成进程间通信。
2. 实现方法
from multiprocessing import Queue
q = Queue(maxsize=0)
功能: 创建队列对象
参数:最多存放消息个数
返回值:队列对象
q.put(data,[block,timeout])
功能:向队列存入消息
参数:data 要存入的内容
block 设置是否阻塞 False为非阻塞
timeout 超时检测
q.get([block,timeout])
功能:从队列取出消息
参数:block 设置是否阻塞 False为非阻塞
timeout 超时检测
返回值: 返回获取到的内容
q.full() 判断队列是否为满
q.empty() 判断队列是否为空
q.qsize() 获取队列中消息个数
q.close() 关闭队列
3>共享内存
1. 通信原理:在内中开辟一块空间,进程可以写入内容和读取内容完成通信,但是每次写入内容会覆盖之前内容。
2. 实现方法
from multiprocessing import Value,Array
obj = Value(ctype,data)
功能 : 开辟共享内存
参数 : ctype 表示共享内存空间类型 ‘i‘ ‘f‘ ‘c‘
data 共享内存空间初始数据
返回值:共享内存对象
obj.value 对该属性的修改查看即对共享内存读写
obj = Array(ctype,data)
功能: 开辟共享内存空间
参数: ctype 表示共享内存数据类型
data 整数则表示开辟空间的大小,其他数据类型 表示开辟空间存放
返回值:共享内存对象
Array共享内存读写: 通过遍历obj可以得到每个值,直接可以通过索引序号修改任意值。
* 可以使用obj.value直接打印共享内存中的字节串
4>本地套接字
1. 功能 : 用于本地两个程序之间进行数据的收发
2. 套接字文件 :用于本地套接字之间通信时,进行数据传输的介质。
3. 创建本地套接字流程
【1】 创建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
【2】 绑定本地套接字文件
sockfd.bind(file)
【3】 监听,接收客户端连接,消息收发
listen()-->accept()-->recv(),send()
5>信号量(信号灯集)
1. 通信原理:给定一个数量对多个进程可见。多个进程都可以操作该数量增减,并根据数量值决定自己的行为。
2. 实现方法
from multiprocessing import Semaphore
sem = Semaphore(num)
功能 : 创建信号量对象
参数 : 信号量的初始值
返回值 : 信号量对象
sem.acquire() 将信号量减1 当信号量为0时阻塞
sem.release() 将信号量加1
sem.get_value() 获取信号量数量
三、线程编程(Thread)
线程基本概念
1. 什么是线程
【1】 线程被称为轻量级的进程
【2】 线程也可以使用计算机多核资源,是多任务编程方式
【3】 线程是系统分配内核的最小单元
【4】 线程可以理解为进程的分支任务
2. 线程特征
【1】 一个进程中可以包含多个线程
【2】 线程也是一个运行行为,消耗计算机资源
【3】 一个进程中的所有线程共享这个进程的资源
【4】 多个线程之间的运行互不影响各自运行
【5】 线程的创建和销毁消耗资源远小于进程
【6】 各个线程也有自己的ID等特征
threading模块创建线程
【1】 创建线程对象
from threading import Thread
t = Thread()
功能:创建线程对象
参数:target 绑定线程函数
args 元组 给线程函数位置传参
kwargs 字典 给线程函数键值传参
【2】 启动线程
t.start()
【3】 回收线程
t.join([timeout])
线程对象属性
t.name 线程名称
t.setName() 设置线程名称
t.getName() 获取线程名称
t.is_alive() 查看线程是否在生命周期
t.daemon 设置主线程和分支线程的退出关系
t.setDaemon() 设置daemon属性值
t.isDaemon() 查看daemon属性值
daemon为True时主线程退出分支线程也退出。要在start前设置,通常不和join一起使用。
自定义线程类
1. 创建步骤
【1】 继承Thread类
【2】 重写__init__方法添加自己的属性,使用super加载父类属性
【3】 重写run方法
2. 使用方法
【1】 实例化对象
【2】 调用start自动执行run方法
【3】 调用join回收线程