天天看点

# 进程和线程进程和线程利用OS模块创建进程避免僵尸进程的方法multiprocessing模块

进程和线程

一家公司就类似一个程序, 进程是公司下属部门,是资源分配的最小单位, 线程则像是部门下属员工

部门之间通信需要建立通道(管道\共享内存)或者公告板(消息队列),

员工之间通信,基本靠吼(全局变量),牛逼的员工也可做到和一个部门(进程)同样的能力

概念

1、多任务编程
	1、意义 : 充分利用计算机的多核资源,同时运行多个任务,提高程序执行的效率
	2、实现方法 : 多进程、多线程
2、概念
	1、并发 : 同时处理多个任务,交替进行,内核在任务间不断切换,达到好像多个任务同时被执行的效果,实际上在每一时刻里,只有一个任务在占用cpu
	2、并行 : 同一时间同时进行多个任务,一起执行,多个任务利用计算机的多核资源同时执行,此时多个任务之间是并行关系
	3.在计算机系统中,并行和并发是同时存在的
进程和程序的定义和区别
	1、进程 : 程序在计算机中的1次运行过程
	2、程序 : 可执行文件,静态的,占用磁盘
	3、区别 : 进程是一个动态的过程,占用计算机资源(CPU、内存),有生命周期
 程序 : 为1个文件,战友外存储器(磁盘)
进程的诞生
	1、用户通过启动程序,或者调用进程创建接口发起请求
	2、操作系统接收用户请求,开始创建进程
	3、操作系统分配计算机资源,确定进程状态,开辟空间等
	4、把进程提供给用户使用
           

进程

1、cpu时间片
	如果一个进程占用cpu,则称这个进程在CPU事件片上或称这个程序得到了CPU事件片,谁获取到了CPU时间片,谁就可以执行,进程等待
2、PCB(进程控制块) 
	在内存中开辟的一块空间,存放进程的基本信息,也是操作系统调用进程的重要标志.
3、进程ID(PID)
	操作系统为每一个进程分配一个不重复的ID号,该进程在系统中的标识
	查看进程信息 : ps -aux
4、父子进程 : 
	系统中每个进程都有唯一的父进程
	1、查看进程树 : pstree
	2、示例 : 写一个程序,每隔两秒打印一下时间
	3、1个终端运行程序,在另一个终端执行 ps -aux
5.进程状态
	1、三态 : 
	就绪态
		具备执行条件,等待系统分配CPU事件片
	运行态
		占有了cpu时间片
	等待态
		进程暂时祖阻塞,不具备执行条件
	2、五态 :
	新建态:
		创建新进程获取资源的过程
	终止态
		结束进程,释放资源的过程
	3、查看进程状态 : ps -aux 查看STAT列

	4、进程的优先级
		1、作用 : 决定进程的优先权限和占有资源的优先程度
		2、Linux优先级范围(40级) -20~19 越小越高
			默认优先级为0
		3、查看优先级的命令 : top 
		NI 这一列就是优先级 shift -+翻页
		指定优先级 : nice --20 python3 xxxx
           

进程状态

查看进程状态 : ps -aux 查看STAT列
S : 等待态 (sleep,可中断等待态,阻塞函数),可中断阻塞(阻塞函数)
D : 等待态(不可中断,系统进程),不可中断阻塞
T : 暂停态(后台程序,暂停执行,但是没有结束)
R : 运行态
Z : 僵尸进程  
< : 较高的优先级
N : 较低的优先级
+ : 前台进程(在终端运行,能够被终端控制的进程)
           

小技巧

如何后台执行程序
	firefox & - 让程序在后台运行
	如何杀死进程
	kill -9 PID进程号
	如何查找进程PID号
	ps -aux | grep '进程名'
	查看进程信息 : ps -aux
	查看进程树 : pstree
           

进程的运行特征

1.可使用计算机多核资源
2.计算机分配资源的最小单位(CPU、内存)
3、每个进程空间独立,有自己的资源
4、进程之间的运行互不干扰,相互独立
           

利用OS模块创建进程

1、方法名 : pid = os.fork()

功能 : 创建新的进程
	返回值
		1、创建进程失败 : 返回负数
		2、创建进程成功
			1、原进程中返回新进程的PID号
			2、新进程中返回0
           

2、注意

用fork()执行结果可能不一致,因为虽然是父子进程,但不存在谦让关系,谁先抢到cpu时间片谁先执行
	各个进程间,资源空间相对独立,不会互相干扰
           

3.关于fork()

子进程从fork()下一句开始
	父子进程各自独立运行,运行顺序不一定:
	if 结构和fork()固定搭配
	子进程会复制父进程全部代码和空间,包括fork()之前的资源
	父子进程空间互不影响,各自修改各自空间内容
           

4.进程函数(os)

os.getpid()
		获取当前进程的PID号
	os.getppid()
		获取父进程的PID号
	os._exit(status)
		功能 : 退出当前进程
		参数 : status 0 正常 1 异常
		示例 : os._exit(0)
	sys.exit(status)
		功能 :退出当前进程
		参数 : 默认为0 ,可以已传字符串并输出在终端上
		示例 : sys.exit('进程已退出')
           

5、孤儿进程

父进程先于子进程退出,此时子进程就是孤儿进程(会被系统进程收养)
           

6、僵尸进程 : Z

1、子进程先结束,而且父进程没有处理子进程退出行为,此时子进程为僵尸进程
	2、大量僵尸进程后果: 虽然结束,但是会存留部分进程信息在内存中,耗费系统资源
           

避免僵尸进程的方法

1、让父进程来处理子进程

pid,status = os.wait()
	功能 : 分进程中阻塞,等待子进程退出并处理
	返回值 : pid - 已退出的子进程的PID好
        status - 子进程的退出状态(0正常,1异常)
           

2、二级子进程

让一级子进程先退出,二级子进程变为孤儿进程,被系统进程收养,再让父进程处理一级子进程
的退出,通过将一级子进程直接杀死,使得父进程和二级子进程互无关联,而避免了僵尸进程的出现
if __name__ == '__main__' :
pid = os.fork()
if pid <0:
    print('一级进程创建失败')
elif pid == 0:
    
    p = os.fork()
    if p < 0:
        print('二级进程创建失败')
    elif p == 0:
        print('二级进程创建成功')
        copy2()  #事件2 二级子进程
    else:
        sys.exit('一级子进程退出')
else:
    pid,status = os.wait()
    print('一级子进程处理完毕')
    copy1()  # 事件1 父进程
           

multiprocessing模块

multiprocessing

一、基本格式

1、导入模块

form multiprocessing import Process

2、将需要执行的进程事件封装为函数

def fun()1… # 不能用input()函数

3、使用Process类生成进程对象,并关联相关函数

if name == ‘main’

p = Process(target = fun1)

4、通过进程对象启动进程(satrt()),自动执行进程函数

p.start()

5、回收进程(避免产生僵尸进程)

p.join()

二、process 类

功能 :

创建进程对象

参数(传给(__init(…))

1.targer = 函数名() : p =process(tager = fun1)

2 . name = ‘进程名’ : 给进程起名字,默认Process -1

3.args = (元祖) : 元祖传参(按照位置给target函数传参)

4. kwargs = {字典} : 字典传参(按照键值对给target传参)

例子:

def fun1(name,age):

p=Process(target = fun1,agre(‘郭德纲’,‘45’))

p = process(target = fun1,kwargs =({“name”:“于谦”,“age” : 45})

p.join([timeout]) : 阻塞等待回收子进程,超时时间,如果超过时间,则解除阻塞
三、Process 类创建进程
	1、同样子进程复制父进程全部空间,父子进程互不影响
	2、join()作用:
		阻塞,处理子进程,等所有子进程结束后执行父进程后面语句
	3、start()后面最好加join(),避免子进程先结束为僵尸进程
	4、父子进程同时做事在start()和join()中间写代码
四、多进程
	1、父进程一般只用来创建子进程和回收子进程,具体事务让子进程来做
           

父进程 : 负责创建进程、回收进程

子进程们 : 各自执行各自的功能函数

##牺牲了1个进程(父进程)的代价来处理僵尸进程

2、multiprocessing中父进程更方便的创建多个子进程执行更多个事件,父进程往往将事件交由子进程去完成

五、进程中函数的传参

1、元祖传参

args=()

2、字典传参

kwargs = {‘参数名’:要传的值}

6、进程对象 P 属性

1、p.name :进程名称

2、p.pid : 进程pid号

3、p.is_alive : 进程状态

4、p.daemon : 守护进程,默认False,主进程结束,不影响子进程

设置为true ,主进程退出,子进程全部结束

5.daemon不要和join同时使用

守护进程一般为系统后台进程

生命周期很长(开/关机)

7.自定义进程类

1、使用情况 : 使用类将一系列功能进行封装,完成比较复杂的功能

2、使用流程
		1、集成Process类, Process类中属性和方法就有了
		2、编写自己的__init__()函数
		3、重写run方法,完成功能的逻辑调用(名字不能变)
		4、创建实例化对象
		5、用对象调用一个start()来创建新进程, 自动执行run()方法
		6、用进程对象调用join()方法回收进程
8、进程池
	1、多进程优缺点
		1、优点 : 利用多核资源, 同时执行多任务, 提高效率
		2、缺点 : 进程创建和删除需要消耗较多的系统资源, 
           

大量进程频繁创建和删除会给系统带来压力

2、进程池

1、产生原因 : 应对大量任务,需要多进程完成, 需要频繁创建和销毁进程的情况

2、原理 : 创建一定量的进程作为进程池, 用来处理事件, 事件处理完成后进程不退出, 而是继续等待处理其他事件, 直到所有待处理事件处理完成后统一销毁, 增加了进程的重复利用率,降低资源消耗.

3、使用流程

1、创建进程池, 放入时长数量的进程

2、将要做的实景封装成函数,放入进程池的等待队列

3、进程池中国的进程不断执行任务,直到全都被执行

4、关闭进程池, 回收进程

4、常用方法

1、类名 : from multiprocessing import pool

2、pool = Pool(porcesses = n) 创建n个进程

功能 : 创建进程池对象

参数 : 指定进程池中进程数量,默认根据系统自动判定(可不写,根据cpu核心数量)

3、想进程池中添加任务的方法

1.poo.apply_async(func,args,kwds)

功能 : 放入要执行的事件函数(异步非阻塞)

解释 : 异步, 交代完任务就去做别的,不会等

参数 : func 事件函数

args 元祖传参

kwds 字典传参

2.poo.apply(func,args,kwds)

功能 : 放入要执行的事件函数(同步阻塞)

解释 : 同步,进程池中只能有1个进程在执行,退出1个,进来1个

参数 : func 事件函数

args 元祖传参

kwds 字典传参

返回值 : 返回函数事件对象(获取到func返回值)

3、poo.map(func,iter)

功能 : 放入要执行的事件函数(同步阻塞)

解释 : 同步,进程池中只能有1个进程在执行,退出1个,进来1个

参数 : func 事件函数

args 元祖传参

kwds 字典传参

返回值 : 返回函数事件对象(获取到func返回值)

获取方法 :

1.robj = pool.apply_async(func,args=(i,))

2.robj.get()

4、pool.close()

功能 : 关闭进程池, 无法再添加新的任务

5、pool.join()

功能 : 阻塞等待回收进程池

继续阅读