前情回顾
1. 进程对象属性
p.pid p.name p.is_alive() p.daemon
2. 自定义进程类
继承Process 重写run
3. 进程池 大量进程事件需要频繁创建删除进程
Pool()
apply_async()
close()
join()
map()
4.进程间通信
管道 消息队列 共享内存 信号 信号量 套接字
管道: Pipe() fd.recv() fd.send()
消息队列: Queue() q.get() q.put() q.full()
q.empty() q.qsize() q.close()
共享内存: Value() Array()
信号 : kill -l
kill -sig PID
kill(pid,sig)
*****************************************************
import signal
signal.alarm(sec)
功能 : 向自身发送时钟信号 --》 SIGALRM
参数 : sec 时钟时间
* 进程中只能有一个时钟,第二个会覆盖第一个时间
同步执行 : 按照顺序逐句执行,一步完成再做下一步
异步执行 : 在执行过程中利用内核记录延迟发生或者准备 处理的事件。这样不影响应用层的持续执行。 当事件发生时再由内核告知应用层处理
* 信号是唯一的异步通信方法
signal.pause()
功能:阻塞等待接收一个信号
signal.signal(signum,handler)
功能: 处理信号
参数: signum 要处理的信号
handler 信号的处理方法
SIG_DFL 表示使用默认的方法处理
SIG_IGN 表示忽略这个信号
func 传入一个函数表示用指定函数处理
def func(sig,frame)
sig: 捕获到的信号
frame : 信号对象
信号量(信号灯)
原理 : 给定一个数量,对多个进程可见,且多个进程都可以操作。进程通过对数量多少的判断执行各自的行为。
multiprocessing --》 Semaphore()
sem = Semaphore(num)
功能: 创建信号量
参数: 信号量初始值
返回: 信号量对象
sem.get_value() 获取信号量值
sem.acquire() 将信号量减1 当信号量为0会阻塞
sem.release() 将信号量加1
进程的同步互斥
临界资源 :多个进程或者线程都能够操作的共享资源
临界区 : 操作临界资源的代码段
同步 : 同步是一种合作关系,为完成某个任务,多进程或者多线程之间形成一种协调,按照约定或条件执行操作临界资源。
互斥 : 互斥是一种制约关系,当一个进程或者线程使用临界资源时进行上锁处理,当另一个进程使用时会阻塞等待,直到解锁后才能继续使用。
Event 事件
multiprocessing --》 Event
创建事件对象
e = Event()
设置事件阻塞
e.wait([timeout])
事件设置 当事件被设置后e.wait()不再阻塞
e.set()
清除设置 当事件设置被clear后 e.wait又会阻塞
e.clear()
事件状态判断
e.is_set()
Lock 锁
创建对象
lock = Lock()
lock.acquire() 上锁 如果锁已经是上锁状态调用此函数会阻塞
lock.release() 解锁
with lock: 上锁
....
....
解锁
线程
什么是线程
线程也是一种多任务编程方法,可以利用计算机多核资源完成程序的并发执行。线程又被称为轻量级的进程。
线程特征
* 线程计算机多核分配的最小单位
* 一个进程可以包含多个线程
* 线程也是一个运行的过程,消耗计算机资源,多个线程共享进程的资源和空间
* 线程的创建删除消耗的资源都要远远小于进程
* 多个线程之间执行互不干扰
* 线程也有自己的特有属性,比如指令集 ID
threading 模块创建线程
threading.Thread()
功能 : 创建线程对象
参数 :name 线程名称 默认 Thread-1
target 线程函数
args 元组 给线程函数位置传参
kwargs 字典 给线程函数键值传参
t.start() 启动线程 自动运行线程函数
t.join([timeout]) 回收线程
线程对象属性
t.is_alive() 查看线程状态
t.name 线程名称
t.setName() 设置线程名称
t.getName() 获取线程名称
threading.currentThread() 获取当前线程对象
t.daemon 属性
默认情况主线程退出不会影响分支线程执行
如果设置为True 则分支线程随主线程退出
设置方法:
t.daenon = True
t.setDaemon(True)
判断属性值
t.isDaemon()
*Daemon 要在start前设置,不会和join同用
创建自己的线程类
步骤:
1.继承Thread
2.加载Thread中的__init__
3.重写run方法
线程通信
通信方法: 多个线程共享进程的空间,所以线程间通 信使用全局变量完成。
注意事项: 线程间使用全局变量往往要同步互斥机制 保证通信安全
线程同步互斥方法
线程的event
e = threading.Event() 创建事件对象
e.wait([timeout]) 如果e为设置状态则不阻塞否则阻塞
e.set() 将e变为设置状态
e.clear() 清除设置
e.is_set() 判断当前状态
线程锁
lock = threading.Lock() 创建锁对象
lock.acquire() 上锁
lock.release() 解锁
* 也可以通过with上锁,上锁状态调用acquire会阻塞
python线程的GIL问题 (全局解释器锁)
python--》支持多线程--》同步互斥--》加锁--》
超级锁,给解释器加锁--》解释器同一时刻只能解释一个线程
后果 : 一个解释器同一时刻只能解释执行一个线程,所以导致python线程效率低下。但是当遇到IO阻塞时线程会主动让出解释器,因此python线程更加适合高延迟的IO程序并发。
解决方法:
* 尽量用进程完成并发
* 不适用c解释器 c# java
* 尽量使用多种方案组合的方式进行并发操作,线程用作高延迟IO
效率测试
Line cpu: 9.014907121658325
Line IO: 4.548823118209839
thread cpu: 9.38966417312622
thread IO: 4.6143529415130615
Process cpu: 5.466824531555176
Process IO: 2.9468178749084473