1.进程理论
程序:一堆代码
进程:正在运行的程序
进程是一个实体。每一个进程都有它自己独立的内存空间,是系统进行资源分配和调度的基本单位
同步和异步 *****
针对任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事!
异步:提交任务之后,不等待任务的返回结果,直接执行下一步操作
阻塞与非阻塞 *****
针对程序运行的状态
阻塞:遇到io操作(文件读写....) >>> 阻塞态(Blocked)
非阻塞:就绪或者运行态 >>> 就绪态(Ready),运行态(Running)

先来先服务调度算法 FCFS #适合于CPU繁忙型作业,而不利于I/O繁忙型的作业(进程)。 短进程优先调度算法 SJ/PF #既可用于作业调度,也可用于进程调度。但其对长作业不利;不能保证紧迫性作业(进程)被及时处理 时间片轮转法 RR,Round Robin #让每个进程在就绪队列中的等待时间与享受服务的时间成比例 #对这些进程区别对待,给予不同的优先级和时间片,可以进一步改善系统服务质量和效率 多级反馈队列调度算法 #(1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。 #(2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。 #(3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。
2.开启进程的两种方式(***)
本质:在内存中申请一块独立的内存空间
windows下创建进程内部实现:
以模块导入的方式将代码拷贝到新申请的内存空间中
创建进程的开销比较大:
申请内存空间
拷贝代码
创建进程的方式 1:
自己定义一个函数,调用Process(target='自己写的函数',args=(v1,))
from multiprocessing import Process import time
def task(name): print('%s is running'%name) time.sleep(3) print('%s is over'%name) # 注意,在windows系统中,创建进程会将代码以模块的方式从头到尾加载一遍 # 一定要写在if __name__ == '__main__':代码块里面 # 强调:函数名一旦加括号,执行优先级最高,立刻执行 if __name__ == '__main__': p1 = Process(target=task,args=('Tom',)) # 这一句话只是实例化了一个Process对象 p1.start() # 告诉操作系统创建一个进程 print('主程序') >>>: 主程序 Tom is running Tom is over
创建进程的方式 2
写一个类继承Process,在里面固定写一个run方法
from multiprocessing import Process import time class MyProcess(Process): def __init__(self, name): super().__init__() self.name = name # 必须写run方法 def run(self): print('%s is running' % self.name) time.sleep(1) print('%s is end' % self.name) if __name__ == '__main__': obj = MyProcess('Tom') obj.start() print('主程序') >>>: 主程序 Tom is running Tom is end
3.进程对象的join方法(***)
from multiprocessing import Process import time def task(name, n): print('%s is running' % name) time.sleep(n) print('%s is over' % name) if __name__ == '__main__': start_time = time.time() p_list = [] for i in range(3): p = Process(target=task, args=('子进程%s' % i, i)) p.start() p_list.append(p) for i in p_list: i.join() print('主进程', time.time() - start_time) # join的作用仅仅只是让主进程等待子进程的结束,不会影响子进程的运行 >>>: 子进程0 is running 子进程0 is over 子进程1 is running 子进程2 is running 子进程1 is over 子进程2 is over 主进程 2.10233998298645 # if 初始写法 ------------------------------ if __name__ == '__main__': start_time = time.time() p1 = Process(target=task,args=('egon',1)) p2 = Process(target=task,args=('jason',2)) p3 = Process(target=task,args=('kevin',3)) start_time = time.time() p1.start() # 千万要知道,这句话只是告诉操作系统需要进程 p2.start() p3.start() # p1.join() # 主进程等待子进程结束 才继续运行 p3.join() p1.join() p2.join() print('主进程', time.time() - start_time)
4.进程之间内存隔离(***)
# 记住 进程与进程之间数据是隔离的!!! from multiprocessing import Process x = 100 def task(): global x x = 1 print('task - x :', x) if __name__ == '__main__': p = Process(target=task) p.start() p.join() print('主 - x:', x) >>>: task - x : 1 主 - x: 100
5.进程对象其他相关方法
查看进程id号
current_process().pid >>> current_process()返回的是Process()
os.getpid() os.getppid()
terminate() 主动发起杀死进程的操作
time.sleep(0.1)
is_alive() 判断进程是否存活
from multiprocessing import Process,current_process import time import os def task(): print('%s is running'%os.getpid()) time.sleep(3) print('%s is over'%os.getppid()) if __name__ == '__main__': p1 = Process(target=task) # 函数名(),执行优先级最高 p1.start() p1.terminate() # 杀死子进程 time.sleep(0.1) print(p1.is_alive()) # 判断子进程是否存活 print('主') >>>: False 主
6.僵尸进程与孤儿进程
子进程结束之后不会立即释放pid等资源信息
任何进程都会步入僵尸进程,当主进程不停的创建子进程的时候,会有害
两种情况下会回收子进程的pid等信息:
1.父进程正常结束
2.join方法
孤儿进程:父进程意外意外死亡
linux下:
init孤儿福利院;用来回收孤儿进程所占用的资源
ps aux |grep 'Z' (查看孤儿进程)
7.守护进程
主进程一旦运行完毕,子进程立即结束运行(一起死!)
from multiprocessing import Process import time def task(name): print('%s 正活着' % name) time.sleep(3) print('%s 正常死亡' % name) if __name__ == '__main__': p = Process(target=task, args=('总管',)) # 容器型:只有一个元素,加逗号"," p.daemon = True # 必须在p.start开启进程命令之前声明 p.start() print('皇帝正在死亡') >>>: 皇帝正在死亡
8.互斥锁(***) 锁千万不要随意去用
实质:加锁会将并发变成串行
作用:解决多个进程操作同一份数据,造成数据不安全的情况
结果:牺牲了效率但是保证了数据的安全
位置:锁一定要在主进程中创建,给子进程去用
作用点:锁通常用在对数据操作的部分,并不是对进程全程加锁
from multiprocessing import Process, Lock import json import time import random def search(i): with open('info', 'r', encoding='utf-8') as f: data = json.load(f) print('用户查询余票数:%s' % data.get('ticket')) def buy(i): # 买票之前还得先查有没有票! with open('info', 'r', encoding='utf-8') as f: data = json.load(f) time.sleep(random.randint(1, 3)) # 模拟网络延迟 if data.get('ticket') > 0: data['ticket'] -= 1 # 买票 with open('info', 'w', encoding='utf-8') as f: json.dump(data, f) print('用户%s抢票成功' % i) else: print("用户%s查询余票为0" % i) def run(i, mutex): search(i) mutex.acquire() # 抢锁 一把锁不能同时被多个人使用,没有抢到的人,就一直等待锁释放 buy(i) mutex.release() # 释放锁 if __name__ == '__main__': mutex = Lock() for i in range(10): p = Process(target=run, args=(i, mutex)) p.start() # info 内容: {"ticket": 1} >>>: 用户查询余票数:1 用户查询余票数:1 用户查询余票数:1 用户0抢票成功 用户1查询余票为0 用户2查询余票为0