进程
进程
一:概念梳理
程序:硬盘中的文件.
进程:程序运行起来之后,以进程为单位在内存中.是资源分配的最小单位,将不同程序之间的内存隔离开,数据不会错乱.
线程:是cpu调度的最小单位.
一个进程里面最少一个线程.
计算机就干两个活:IO和运算
cpu切换进程,会把当前线程的数据和状态保存到内存中才会切换到另一个进程.
并发的本质:切换+保存状态+运算
二:python多进程模块
实现多进程两种方式:面向函数 面向对象
无论哪一种其本质都是:如何定义一个任务,然后如何交给一个工具并发的执行任务.
然后是解决效率的问题.
进程用到multiprocessing模块
面向函数:将任务定义在函数中,然后将任务交给进程最后启动就好了
import os from multiprocessing import Process import time def f1(args): print(args) print("父进程:",os.getppid()) time.sleep(1) print("1") def f2(): print("父进程:", os.getppid())# 查看父进程 time.sleep(1) print("2") if __name__ == "__main__": # 将一个函数注册到一个进程当中,一旦start之后就开始调用这个函数 # 主进程一定会等所有子进程结束了才结束 #将任务丢到进程中 #参数有两种方式: #1元组一个参数要加, #2字典,相当于关键字传参 #对象是特征与技能的结合体 p1 = Process(target=f1,args=("传进来的参数",)) p2 = Process(target=f1,kwargs={"args":"字典传参"}) p3 = Process(target=f2)
#告诉os开启进程,至于什么时候开启全看os的调度,因此前面创建的进程对象只是传入函数引用,只有开启子进程之后才会调用 p1.start()#异步非阻塞,父进程将开子进程的请求发送到os之后就不管了,继续向下执行,也管不了,因为os负责调度和协调 p2.start() p3.start() print("当前进程号:", os.getpid()) print(__name__) #join方法写在哪个进程中,哪个进程就会等待调用此方法进程的结束才继续执行 # 需求等所有子进程结束了才打印下面的程序结束 p1.join()#同步阻塞,同步只等待p1进程的结束,阻塞只cpu在这里是不工作的,一直等到p1结束之后,cpu才会调度主进程 p2.join() # 前面负责开启进程,是异步执行你无法干预全凭cpu调度,最后join变成同步 # 前面所有子进程结束了,才能输出程序结束 print("程序结束")
主进程会等待所有子进程结束后才结束,负责收尾工作.
守护进程会在主进程结束之后结束.
面向对象:将任务定义在run方法中,创建进程对象之后的start方法会请求os开进程,然后调用run()
import os from multiprocessing import Process class MyProcess(Process): #参数封装 def __init__(self,arg1,arg2): #之所以要调用父类的初始化方法,是因为父类中的属性子类要用到,例如pid,name,父类中已经做好了,直接拿来用然后封装个性化属性 super().__init__() self.__arg1 = arg1 self.__arg2 = arg2 def run(self): #把运行逻辑写到run方法里面就可以了 #此时的参数通过在父进程创建此类的对象,通过init把参数接收进来,到run中执行拿到的参数 print(os.getpid())#在父类中查看进程id只能用os模块 print(self.name)#父类封装了name属性 print(self.pid)#父类封装了pid属性 print(self.__arg1,self.__arg2) if __name__ == "__main__": p1 = MyProcess(1,2) p2 = MyProcess(3,4) p1.start() p2.start()
除了例子中的属性外,父类还封装了方法,可以直接拿来使用
#p.terminate()发指令给os终止子进程,并不能立刻终止,可以你发完此命令在判断isalive()子进程还活着 #p.isalive()判断子进程是否还活着
#这两个方法都是在父类里面调用的,因为父进程开的子进程,子进程引用在父进程手里握着,并不是子进程来调用的.
#p.pid主进程里看子进程id号,在子进程中看只能用os.getpid()看 #父进程中直接打印进程的pid属性即可 ####僵尸进程和孤儿进程 #主进程代码执行完会等待子进程运行结束才会结束 #父进程开子进程,运行角度看是完全独立,没有任何关系 #僵尸进程:进程运行结束之后,保存他的pid,使用了多少内存等这些信息,为了让父进程无论什么时候都能看到这些信息 #僵尸进程是有害的占用着pid号,一旦到了边界,再就开不了进程 #所以如果主进程在子进程消亡后还有很长一段时间不会死掉,那么就会产生大量僵尸进程占用进程号 #因此如果出现这种情况,应在主进程长时间等待过程中,是子进程已经结束后再调用join()把子进程回收掉 #而不是让主进程阻塞等待子进程结束,而是子进程已经结束了,再join() #这样父进程能够在它或者的时候任意看到子进程的信息,但是父进程不知道子进程什么时候死掉 #为了父进程无论子进程或者还是死掉,把子进程消失后的信息还是会保存下来,直到父进程也消失才会全部消失在内存 #父进程死了之后为儿子进程收尸,只要父进程不死随时可以看儿子进程的信息 #所以所有的子进程都会进入僵尸进程 ###孤儿进程 #儿子没死,父进程先死了,子进程就会变成孤儿进程,由政府进程linux中的init进程,进程号为0来回收 #之前的join()方法就是父进程在等待子进程死亡为其收尸,但在它没死亡之前不会影响他们的运行和并发
三:进程锁
并发时涉及到不同进程数据的修改,此时需要加锁来保证数据的安全,虽然效率会降低,但是必须以数据安全为前提.
具体代码放到下一章节
看十遍不如自己写一遍!巩固基础,纵横开拓!

浙公网安备 33010602011771号