一个正在被运行的程序就称之为进程,是程序具体执行过程,一种抽象概念
进程来自于操作系统
多进程:多个正在运行的程序.
测试:
import time
while True:
time.sleep(1)
多次运行该文件,就会产生多个python.exe进程,可以通过tasklist来查看运行的程序
pid:系统会给每一个进程分配一个进程编号
验证:
tasklist 用于查看所有的进程信息
taskkill /f /pid pid 该命令可以用于结束指定进程
# 在python中可以使用os模块来获取pid
import os
print(os.getpid())
PPID
当一个进程a开启了另一个进程b时,a称为b的父进程,b称为a的子进程
在python中可以通过os模块来获取父进程的pid
# 在python中可以使用os模块来获取ppid
import os
print("self",os.getpid()) # 当前进程自己的pid
print("parent",os.getppid()) # 当前进程的父进程的pid
如果是在pycharm中运行的py文件,那pycahrm就是这个python.exe的父进程,当然你可以从cmd中来运行py文件,那此时cmd就是python.exe的父进程
进程和程序的区别
程序就是一堆计算机可以识别文件,程序在没有被运行就是躺在硬盘上的一堆二进制
运行程序时,要从硬盘读取数据到内存中,CPU从内存读取指令并执行 ,
一旦运行就产生了进程
一个程序可以多次执行 产生多个进程,但是进程之间相互独立
当我们右键运行了一个py文件时 ,其实启动的是python解释器,你的py文件其实是当作参数传给了解释器
阻塞 : 程序遇到io操作时就进入了阻塞状态
网络IO recv send
非阻塞: 程序正常运行中 没有任何IO操作 就处于非阻塞状态
阻塞 非阻塞 说的是程序的运行状态
并发: 多个任务看起来同时在处理 ,本质上是切换执行 速度非常快
并行: 多个任务真正的同时执行 必须具备多核CPU 才可能并行
并发 并行 说的是 任务的处理方式
就绪态,运行态,和阻塞态
多道技术会在进程执行时间过长或遇到IO时自动切换其他进程,意味着IO操作与进程被剥夺CPU执行权都会造成进程无法继续执行
但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式
而对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为4种形式创建新的进程
关于创建的子进程,UNIX和windows
1.相同的是:进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另外一个进程。
2.不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,从一开始父进程与子进程的地址空间就是不同的。
进程的销毁
-
正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
-
出错退出(自愿,python a.py中a.py不存在)
-
严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)
-
被其他进程杀死(非自愿,如kill -9)
process类常用属性
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动) 强调: 1. 需要使用关键字的方式来指定参数 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name为子进程的名称
方法介绍:
1 p.start():启动进程,并调用该子进程中的p.run() 2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 3 4 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁 5 p.is_alive():如果p仍然运行,返回True 6. p.exitcode:获取退出码
7 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
8 p.name:进程的名称
父进程等待子进程结束后继续执行
案例1:
"""
当你开启了一个子进程 并且给他一个任务 如果你希望知道这个任务什么时候完成 那就需要等待
"""
import time
from multiprocessing import Process
def task(i):
# print('出去了')
time.sleep(2)
print('%s回来了'%i)
if __name__ == '__main__':
start_time=time.time()
p1=Process(target=task,args=(1,))
p2 = Process(target=task, args=(2,))
p3 = Process(target=task, args=(3,))
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
# p3.join()
# p2.join()
# p1.join()
end_time=time.time()
print(end_time - start_time)
print('over')
python中开启子进程的两种方式
方式1:
实例化Process类
from multiprocessing import Process
import time
def task(name):
print('%s is running' %name)
time.sleep(3)
print('%s is done' %name)
if __name__ == '__main__':
# 在windows系统之上,开启子进程的操作一定要放到这下面
# Process(target=task,kwargs={'name':'egon'})
p=Process(target=task,args=('jack',))
p.start() # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态
print('======主')
方式2:
继承Process类 并覆盖run方法
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self,name):
super(MyProcess,self).__init__()
self.name=name
def run(self):
print('%s is running' %self.name)
time.sleep(3)
print('%s is done' %self.name)
if __name__ == '__main__':
p=MyProcess('jack')
p.start()
print('主')
需要注意的是
1.在windows下 开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程
2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管
进程间内存相互隔离
from multiprocessing import Process
import time
name = '千万'
def task():
name = '哇塞'
print('改过了')
print('子进程的%s' % name)
if __name__ == '__main__':
p = Process(target=task)
p.start()
time.sleep(3)
print(name)
僵尸进程指的是,当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源!
僵尸进程的危害:
由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。
在Linux中,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,
在python中,已经封装好了wait操作不需要我们自己清理。
但是系统所能使用的进程号是有限的,如果大量的产生[僵死进程],将因为没有可用的进程号而导致系统不能产生新的进程. 此为僵尸进程的危害,应当避免。