1.第一带计算机 真空管和穿孔卡片 没有进程 没有操作系统 2.第二代计算机 7094 1401 晶体管 批处理系统 输入输出 以及计算设备 不能互联 需要人参与 一批一批的处理 开发效率慢 并且串行执行 3.第三代计算机 集成电路 与多道技术 多终端联机 spooling 同一台机器既能进行科学计算 又能做字符处理 通用计算机 多道技术 解决串行导致的效率低下问题 多用户终端 可以同时为多个用户提供服务 每个用户以为自己独享一台计算机 4.第四代 个人电脑 大规模使用了集成电路,大多都提供了GUI界面
产生背景 ,所有程序串行 导致资源浪费
目的是让多个程序可以并发执行 , 同时处理多个任务
空间复用 指的是 同一时间 内存中加载多个不同程序数据, 每个进程间内存区域相互隔离,物理层面的隔离 时间复用 切换 + 保存 切换条件: 1.一个进程执行过程中遇到了IO操作 切换到其他进程 2.运行时间过长,会被操作系统强行剥夺执行权力 单纯的切换不够,必须在切换前保存当前的状态,以便于恢复执行
一个正在被运行的程序就称之为进程,是程序具体执行过程,一种抽象概念
进程来自于操作系统
程序就是一堆计算机可以识别文件,程序在没有被运行就是躺在硬盘上的一堆二进制
运行程序时,要从硬盘读取数据到内存中,CPU从内存读取指令并执行 ,
一旦运行就产生了进程
一个程序可以多次执行 产生多个进程,但是进程之间相互独立
同步 异步 指的是任务的提交方式
1.同步:任务提交之后 原地等待的任务的执行并拿到返回结果才走 期间不做任何事(程序层面的表现就是卡住了)
2.异步:任务提交之后 不再原地等待 而是继续执行下一行代码(结果是要的 但是是用过其他方式获取)
阻塞 非阻塞 指的是程序的运行状态
阻塞 : 程序遇到io操作是就进入了阻塞状态 ,程序无法继续执行其他代码
本地IO input print sleep read write
网络IO recv send
非阻塞: 程序正常运行中 没有任何IO操作 就处于非阻塞状态 ,或者通过某种方式使程序即时遇到了也不会停在原地,还可以执行其他操作,以提高CPU的占用率
并发 并行 说的是 任务的处理方式
并发: 多个任务看起来同时在处理 ,本质上是切换执行 速度非常快
并行: 多个任务真正的同时执行 必须具备多核CPU 才可能并行
就绪态,运行态,和阻塞态
多道技术会在进程执行时间过长或遇到IO时自动切换其他进程,意味着IO操作与进程被剥夺CPU执行权都会造成进程无法继续执行
创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去,一个进程对应在内存就是一块独立的内存空间
进程与进程之间数据是隔离的 无法直接交互,但是可以通过某些技术实现间接交互
实例化Process,将要执行任务用target传入 from multiprocessing import Process import time def test(name): print('%s is running'%name) time.sleep(2) print('%s is over'%name) # windows创建进程会将代码以模块的方式 从上往下执行一遍 # linux会直接将代码完完整整的拷贝一份 # windows创建进程一定要在if __name__ == '__main__':代码块内创建 否则报错 if __name__ == '__main__': # 创建一个进程对象 p = Process(target=test,args=('lolo',)) # 告诉操作系统帮你创建进程 p.start() print('主')
继承Process,并覆盖run方法, 将任务放入run方法中 from multiprocessing import Process import time class MyProcess(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s is running'%self.name) time.sleep(2) print('%s is over'%self.name) if __name__ == '__main__': p = MyProcess('coco') p.start() print('主')
1.在windows下 开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程 2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管 进程间内存相互隔离
Process的对象具备一个join函数
from multiprocessing import Process import time def test(name,i): print('%s is running'%name) time.sleep(i) print('%s is over'%name) if __name__ == '__main__': p_list = [] for i in range(3): p = Process(target=test,args=('进程%s'%i,i)) p.start() p_list.append(p) for p in p_list: p.join() p1 = Process(target=test,args=('dodo',2)) p2 = Process(target=test,args=('lolo',1)) p1.start() p2.start() p1.join()
from multiprocessing import Process money = 100 def test(): global money money = 888 if __name__ == '__main__': p = Process(target=test) p.start() p.join() print(money)
from multiprocessing import Process,current_process,active_children import os import time def test(name): print('%s is running'%name,current_process().pid) print('%s run'%name,os.getpid(),os.getppid()) time.sleep(1) print('%s is over'%name) if __name__ == '__main__': p = Process(target=test,args=('lolo',)) p.start() p.terminate() # 杀死当前进程 其实是告诉操作系统帮你杀死一个进程 print(p.is_alive()) # 判断进程是否存活 print('主',os.getpid(), os.getppid())
# p.join() # 等待子进程结束
# p.terminate() # 终止进程
# print(p.name) # 进程的名称
# print(p.is_alive()) #是否存活
# p.terminate() # 与start一样 都是给操作系统发送指令 所以会有延迟
# print(p.is_alive())
# print(p.pid)
# print(p.exitcode) # 获取退出码
进程:一个正在运行的程序。
主进程创建守护进程:
1.守护进程会在主进程代码执行结束后就终止,
2.守护进程内无法再开启子进程,否则抛出异常。
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止。
from multiprocessing import Process import time def teat(name): print('%s 在'%name) time.sleep(1) print('%s 不在'%name) if __name__ == '__main__': p = Process(target=teat,args=('coco',)) p.daemon = True p.start() # p.daemon = True time.sleep(0.1) print('老板不在')
孤儿进程
孤儿进程指的是开启子进程后,父进程先于子进程终止了,那这个子进程就称之为孤儿进程
孤儿进程是无害的,有其存在的必要性,在父进程结束后,其子进程会被操作系统接管。
僵尸进程
僵尸进程指的是,当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源!
僵尸进程的危害:
由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。
在Linux中,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,
在python中,已经封装好了wait操作不需要我们自己清理。
但是系统所能使用的进程号是有限的,如果大量的产生[僵死进程],将因为没有可用的进程号而导致系统不能产生新的进程. 此为僵尸进程的危害,应当避免。
互相排斥的锁(如果这个资源已经被锁了,其他进程就无法使用了)
需要强调的是: 锁 并不是真的把资源锁起来了,只是在代码层面限制你的代码不能执行。
使用场景:
并发将带来资源的竞争问题,
当多个进程同时要操作同一个资源时,将会导致数据错乱的问题。
解决方案1: 加join, 例子: from multiprocessing import Process import time def task1(): print('你好,阿森。。。') time.sleep(3) print('吃饭') time.sleep(3) print('下雨') def task2(): print('你好,阿三。。。') time.sleep(3) print('吃面') time.sleep(3) print('下雪') def task3(): print('你好,阿四。。。') time.sleep(3) print('吃米') time.sleep(3) print('下冰雹') if __name__ == '__main__': p1 = Process(target=task1) p2 = Process(target=task2) p3 = Process(target=task3) p1.start() p1.join() p2.start() p2.join() p3.start() p3.join() 弊端: 1.把原本并发的任务变成了穿行,避免了数据错乱问题,但是效率降低了,这样就没必要开子进程了。 2.原本多个进程之间是公平竞争,join执行的顺序就定死了,这是不合理的。 解决方案2: 就是给公共资源加 锁----互斥锁。 例子: from multiprocessing import Process,Lock import time,random def task1(lock): # 上锁 lock.acquire() #就等同于一个if判断 print('你好,阿森。。。') time.sleep(random.randint(0,3)) print('吃饭') time.sleep(random.randint(0, 3)) print('下雨') #解锁 lock.release() def task2(lock): lock.acquire() print('你好,阿三。。。') time.sleep(random.randint(0, 3)) print('吃面') time.sleep(random.randint(0, 3)) print('下雪') lock.release() def task3(lock): lock.acquire() print('你好,阿四。。。') time.sleep(random.randint(0, 3)) print('吃米') time.sleep(random.randint(0, 3)) print('下冰雹') lock.release() if __name__ == '__main__': lock=Lock() p1=Process(target=task1,args=(lock,)) p2=Process(target=task2,args=(lock,)) p3=Process(target=task3,args=(lock,)) p1.start() p2.start() p3.start()
# 注意1: 不要对同一把执行多出acquire 会锁死导致程序无法执行 一次acquire必须对应一次release from multiprocessing import Lock l=Lock() l.acquire() print('抢到了') l.release() l.acquire() print('接着抢') # 注意2:想要保住数据安全,必须保住所有进程使用同一把锁
锁和join的区别:
1. join是固定了执行顺序,会造成父进程等待子进程完成后完成
锁是公平竞争谁先抢到谁先执行,父进程可以做其他事情、
2. join是把进程的任务全部串行
锁可以锁任意代码 ,一行也可以 可以自己调整粒度
粒度:
粒度越大意味着锁住的代码越多 效率越低
粒度越小意味着锁住的代码越少 效率越高
MYSQL 中不同隔离级别 其实就是不同的粒度
小练习 -- 抢票
""" 过程: 1.查看余票, 2.有就买,无失败 os.getpid()获取当前进程id #文件db的内容为:{"count":1} #注意一定要用双引号,不然json无法识别 """ from multiprocessing import Process, Lock import json, random, time, os def check_ticket(): with open('ticket.json', 'rt', encoding='utf-8')as a: data = json.load(a) time.sleep(random.randint(0, 3)) print('%s正在查票,票还有%s' % (os.getpid(), data["count"])) def buy_ticket(): with open('ticket.json', 'rt', encoding='utf-8')as a: data = json.load(a) if data["count"] > 0: data["count"] -= 1 # 模拟延迟 time.sleep(random.randint(0, 3)) with open('ticket.json', 'wt', encoding='utf-8')as a: json.dump(data, a) print('%s恭喜你抢票成功' % os.getpid()) else: print('%s抢票失败,开个加速包试一试' % os.getpid()) def task(lock): check_ticket() lock.acquire() buy_ticket() lock.release() if __name__ == '__main__': lock = Lock() # 三个人抢票 for i in range(10): p = Process(target=task, args=(lock,)) p.start()