进程
2. 进程
[TOC]
程序和进程
'''
1. 程序:一堆代码
2. 进程:进程是系统进行资源分配和调度的基本单位,相当于车间
- 每个进程会自带一个主线程,可以开启多个线程
- 开启进程的开销远大于线程
开启进程时会开辟一个名称空间,每开辟一个进程都会占用一份内存资源
- 可以实现并行
'''
进程调度
'''
1. 先来先服务调度算法(FCFS)
- 根据先后顺序调度,适用于长作业和CPU密集型作业
2. 短作业优先调度算法(SJ/PF)
- 根据用时长短调度,
3. 时间片轮转法(RR)
- 将CPU的处理时间分成固定大小的时间片,在规定时间片内没有执行完,就会释放CPU占用,排到就绪末尾等待下一次调度
4. 多级反馈队列
- 设置多个就绪队列,就绪队列的优先级依次降低
- 就绪队列优先级越高,规定时间片就越小
- 当就绪队列的规定时间片没有执行完时,会放到下一队列的末尾
注意:仅当第一队列空闲时,才会才调度第二队列中的进程运行;
当优先级高的队列进入新进程时,会优先执行新进程,并将当前进程放置当前队列的末尾,
'''
阻塞和非阻塞
'''
1. 状态介绍
- 就绪态:所有进程创建时都会进入就绪态,准备调度
- 运行态:调度后的进程进入运行态
- 阻塞态:凡是遇到IO操作的进程都会进入阻塞态,若IO结束则重新进入就绪态
2. 阻塞和非阻塞
- 阻塞:阻塞态
- 非阻塞:就绪态和运行态
'''
同步和异步
'''
1. 同步
- 一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成
2. 异步
- 不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务
注意
1. 同步异步是指执行任务的方式
2. 阻塞非阻塞是指采取某种执行任务方式后的状态,同步的执行任务就会造成阻塞
'''
串行、并行和并发
'''
1.串行:
- 多个任务时,各个任务按顺序执行,完成一个之后才能进行下一个
2. 并行
- 真正的两者同时执行
- 要具备多个cpu才能并行
3. 并发
- 看上去是同时在执行,实际上是不停在保存状态+切换
- 当执行任务阻塞时就切换执行下一个任务,从而提高效率
注意:同步异步是要求任务之间是否有依赖关系,串行并行不需要依赖关系
'''
创建进程的方式
from multiprocessing import Process
# 1. 方式1
# 定义一个任务
def task(name):
print(f'{name}执行了')
if __name__ == '__main__':
# target为任务名,args用来传参,必须是元组,加逗号
p = Process(target=task,args=('wick',))
p.start()
# 2. 方式2
# 定义一个类,类中的函数必须是run
class MyProcess(Process):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print(f'{self.name}执行了')
if __name__ == '__main__':
p = MyProcess('wick')
p.start()
'''
注意:
- 在window系统下,创建子进程,windows会把父进程代码重新加载一次
- 因此需要使用if __name ==‘__main’判断创建子进程,否则会陷入递归
'''
进程对象的方法
join方法和守护进程
'''
1. join方法
- 子进程结束后再结束父进程
- 必须用在p.start()后
- 只能join住start开启的进程,而不能join住run开启的进程
2. 守护进程
- 主进程结束后,产生的所有子进程也会跟着结束
- 守护进程内无法再开启子进程
- 必须用在p.start()前
'''
from multiprocessing import Process
def task(name):
print(f'{name}的任务已经结束')
if __name__ == '__main__':
p = Process(target = task,args = ('json',))
p.daemon = True # 主进程结束,子进程也结束
p.start()
p.join() # 主进程等子进程执行完再结束
print('主进程')
僵尸进程与孤儿进程
'''
1. 僵尸进程
- 子进程已经结束,但是pid号还存在,没有销毁,会占用pid号,占用操作系统资源
2. 孤儿进程
- 子进程还在执行,但父进程意外结束
- 操作系统优化机制:会回收孤儿进程
'''
其他方法
'''
1. current_process().pid:获取子进程号
2. p.terminate():终止子进程
3. p.is_alive():判断子进程是否存活
4. os.getppid():获取主主进程号
5. os.getppid():获取主主进程号
'''
from multiprocessing import Process
from multiprocessing import current_process
import os
import time
def task(name):
# 1. current_process().pid:获取子进程号
print(f'{name}开始执行任务',current_process().pid)
if __name__ == '__main__':
p = Process(target = task,args = ('json',))
p.start()
p.join()
# 2. p.terminate():终止子进程
p.terminate()
time.sleep(0.1) # 主进程进入阻塞态,子进程进入运行态
# 3. p.is_alive():判断子进程是否存活
print(p.is_alive())
# 4. os.getppid():获取主主进程号
print('主进程',os.getpid())
# 5. os.getppid():获取主主进程号
print('主主进程',os.getppid())
进程互斥锁
'''
1. 进程互斥锁
- 进程之间数据不共享,但是共享同一套文件系统,当并发或并行修改文件时,就会发生错误
- 互斥锁:让并发变成串行,牺牲了执行效率,保证了数据安全
2. mutex = Lock()
- mutex.acquire() 加锁
- mutex.release() 释放锁
'''
# 模拟抢票功能.py
import json
import time
from multiprocessing import Process
from multiprocessing import Lock
# 查看余票
def search(user):
with open('data', 'r', encoding='utf-8')as f:
dic = json.load(f)
print(f'{user}查看余票:{dic.get("ticket")}')
def buy(user):
with open('data', 'r', encoding='utf-8')as f:
dic = json.load(f)
# 模拟网络延迟
time.sleep(1)
if dic.get("ticket") > 0:
dic['ticket'] -= 1
with open('data', 'w', encoding='utf-8')as f:
json.dump(dic, f)
print(f'{user}抢票成功')
else:
print(f'{user}抢票失败')
def run(user, mutex):
search(user)
# 加锁
mutex.acquire()
buy(user)
# 释放锁
mutex.release()
if __name__ == '__main__':
with open('data', 'w', encoding='utf-8')as f:
json.dump({"ticket":1}, f)
mutex = Lock()
for i in range(10):
p = Process(target=run, args=(f'用户{i}',mutex))
p.start()
进程间的通信
进程间数据隔离
from multiprocessing import Process
x = 100
def func():
global x
x = 200
if __name__ == '__main__':
p = Process(target = func)
p.start()
print(x) # 100
print('主进程')
队列(Queue)
'''
1. 队列
- 相当于内存中的一个队列空间,可以存放多个数据,遵循‘先进先出’(管道+锁)
2. 堆栈
- 遵循先进后出
'''
from multprocessing import Queue
q = Queue(3) # 参数代表队列中存放的参数数量,默认无限大
# 1. put:添加数据,超过数量限制就会卡在那里
q.put(1)
# 2. put_nowait:添加数据,超过数量限制就报错
q.put_nowait(2)
# 3. get:获取数据,先进先出,没有会卡住
print(q.get()) # 1
# 4. get_nowait:获取数据,没有则报错
print(q.get_nowait()) # 2
# 5. full:判断队列是否满了
print(q.full())
# 6. empty:判断队列是否为空
print(q.empty:())
进程间通信(IPC)
进程间数据是相互隔离的,若想实现进程间通信,可以利用队列
from multiprocessing import Process
from multiprocessing import Queue
def put(q):
data = '数据'
q.put(data)
print('进程1添加数据到队列中')
def get(q):
data = q.get()
print(f'进程1从队列获取数据{data}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=put, args=(q,))
p2 = Process(target=get, args=(q,))
p1.start()
p2.start()
生产者和消费者模型
'''
1. 什么是生产者和消费者模型
- 生产者消费者模式是通过一个≥容器来解决生产者和消费者的强耦合问题
- 生产者与消费者利用阻塞队列来进行通讯,生产者生成数据后直接丢给阻塞队列,消费者需要数据则从阻塞队列获取,
2. 优势
- 解耦
生产者和消费者依赖于某个缓冲区,两者之间不直接依赖,耦合度低
- 支持并发
- 支持忙闲不均
生产者和消费者处理速率不同时,因为有缓冲区,不会总体处理效率
3. 应用场景
- 应用于解决生产者与消费者的生产与消费的速率不一致的问题,比如用户提交订单场景
'''
from multiprocessing import Queue,Process
import time
# 生产者
def producer(food,q):
for i in range(9):
data = (food,i)
print(f'生产了{data}')
q.put(data)
time.sleep(1)
# 消费者
def consumer(q):
while True:
data = q.get()
if not data:
break
print(f'吃了{data}')
if __name__ == '__main__':
q= Queue()
p1 = Process(target=producer, args=('yox',q,))
c1 = Process(target=consumer, args=(q,))
p1.start()
c1.start()
p1.join() # p1进程执行完后再结束主进程
print('主进程结束')
浙公网安备 33010602011771号