进程的创建与结束
进程的创建:
但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式,一些操作系统只为一个应用程序设计,比如微波炉中的控制器,一旦启动微波炉,所有的进程都已经存在。
而对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为4中形式创建新的进程:
1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)
2. 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)
3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
4. 一个批处理作业的初始化(只在大型机的批处理系统中应用)
无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。
进程的结束
1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,
在linux中用exit,在windows中用ExitProcess)
2. 出错退出(自愿,python a.py中a.py不存在)
3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)
4. 被其他进程杀死(非自愿,如kill -9)
在python程序中的进程操作
运行中的程序就是一个进程。所有的进程都是通过它的父进程来创建的。
因此,运行起来的python程序也是一个进程,那么我们也可以在程序中再创建进程。
多个进程可以实现并发效果
multiprocess模块
仔细说来,multiprocess不是一个模块而是python中一个操作、管理进程的包。 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便大家归类记忆,我将这部分大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。
主进程会在子进程执行完之后,回收子进程的一些资源(内存空间等等),
若主进程先于子进程执行完,会等待子进程执行结束,去回收资源,而不会去杀死子进程(后续也有特殊方法可以强行结束子进程).
PID:
(Process Identification)操作系统里指进程识别号,也就是进程标识符。操作系统里每打开一个程序都会创建一个进程ID,即PID。
PID是各进程的代号,每个进程有唯一的PID编号。它是进程运行时系统分配的,并不代表专门的进程。在运行时PID是不会改变标识符的,但是进程终止后PID标识符就会被系统回收,就可能会被继续分配给新运行的程序。
含义
只要运行一程序,系统会自动分配一个标识。
是暂时唯一:进程中止后,这个号码就会被回收,并可能被分配给另一个新进程。
只要没有成功运行其他程序,这个PID会继续分配给当前要运行的程序。
如果成功运行一个程序,然后再运行别的程序时,系统会自动分配另一个PID。
process模块介绍
使用python都是调用操作系统的命令来启动进程
import os
from multiprocessing import Process #Process是一个类
def func():
print('in func',os.getpid(),os.getppid()) #ppid是父进程的进程识别符
if __name__ == '__main__':
print('in main',os.getpid(),os.getppid())
p1=Process(target=func) #进程对象
p1.start()#向操作系统提交了一个开启子进程的申请
#输出:
# in main 10592 7924
# in func 10772 10592
#其中,7924是pycharm的pid,也就是ppid,10592是这个07-16.py程序执行时的pid,
#所以新建一个进程后,新进程的pid是10772,原来的子进程(07.16.py)就变为了父进程了,
#只要pycharm不结束进程,每次执行,他的pid就是7924,但是每执行一下07-16.py的pid就变一下,
#模仿 p.start()实现的过程,简单模仿,可能不太对,
class MyP:
def __init__(self, func):
self.func = func
def func(self):
print(666)
def run(self):
self.func()
def start(self):
self.run()
p = MyP(func)
p.start()
执行:p1.start(之后),向操作系统提交一个开启子进程的申请,会重新开辟一个内存空间,
新开辟的内存空间会import一个父进程的文件,相当于执行这个父文件的代码,把代码加载到新开启的进程的内存空间(把变量,函数等等加载到新开辟的内存空间),遇到:if __name__ == '__main__':则判断不成立,但是,可以执行func函数了(执行p1.func()),
假如没有if __name__ == '__main__',新开辟的内存空间依然会执行p1.start()代码,又提交一个开启子进程的申请,就相当于进入了一个死循环,系统是不允许这样做的.
同样使用python 不同的操作系统的操作是不同的
对于windows来说 必要加if __name__ == '__main__':
对于linux ios来说 不必要加if __name__ == '__main__':
给子进程传参:传参必须为元组类型
import os
from multiprocessing import Process
def func(num):
print('in func',num,os.getpid(),os.getppid())
if __name__ == '__main__':
print('in main',os.getpid(),os.getppid())
p1=Process(target=func,args=(1,)) #传参,args必须为元组类型
p1.start()#向操作系统提交一个开启子进程的申请,
p2=Process(target=func,args=(2,))
p2.start()
#输出:
# in main 6836 7924 其中,7924是pycharm的pid,6836是文件(执行时的进程)的pid
# in func 1 6760 6836 其中,6836是07-16.py文件的pid,6760是新开进程的pid
# in func 2 3516 6836 其中,6836是07-16.py文件的pid,3516是新开另一个进程的pid
开启多个子进程的方法:
import os
from multiprocessing import Process
def func(num):
print('in func',num,os.getpid(),os.getppid())
if __name__ == '__main__':
print('in main',os.getpid(),os.getppid())
for i in range(10):
p = Process(target=func,args=(i,))
p.start() # start不是运行一个程序,而是调用操作系统的命令,要创建子进程
print('主进程 的 代码执行结束了')
# 输出:
# in main 11252 7924
# 主进程 的 代码执行结束了
# in func 0 11092 11252
# in func 1 7692 11252
# in func 3 920 11252
# in func 4 6464 11252
# in func 7 9796 11252
# in func 2 8244 11252
# in func 8 6268 11252
# in func 9 9748 11252
# in func 5 11248 11252
# in func 6 11084 11252
join方法:阻塞,直到p这个子进程执行完毕之后再继续执行后面的代码
import os
import time
from multiprocessing import Process
def func(num):
print('in func',num,os.getpid(),os.getppid())
if __name__ == '__main__':
print('in main',os.getpid(),os.getppid())
for i in range(5):
p = Process(target=func,args=(i,))
p.start() # start不是运行一个程序,而是调用操作系统的命令,要创建子进程
p.join() #阻塞,直到p这个子进程执行完毕之后再继续执行
print('主进程 的 代码执行结束了')
输出:
#in main 9388 7924
#in func 1 8532 9388
#in func 2 11060 9388
#in func 4 5748 9388
#in func 3 9068 9388
#主进程 的 代码执行结束了
#in func 0 9440 9388
为什么会出现,没有把子进程join住的现象?
因为最后一个子进程肯定阻塞住了(等子进程执行完,再继续执行),
但是前面开启的子进程,有可能cpu来不及处理,时间慢了,就最后执行了,虽然join住了大部分的子进程,可还是要有子进程没有阻塞住,
解决办法
import os
from multiprocessing import Process
def func(num):
print("in func", num, os.getpid(), os.getppid())
if __name__ == '__main__':
print('in main', os.getpid(), os.getppid())
lst = []
for i in range(10):
p = Process(target=func, args=(i,))
p.start() # 照常开启子进程,
lst.append(p)
for p in lst:
p.join() # 拿到每个子进程,分别进行阻塞,
print('主进程执行完毕')
输出:
in main 8004 1848
# in func 3 8972 8004
# in func 2 10100 8004
# in func 7 9304 8004
# in func 6 9372 8004
# in func 4 9300 8004
# in func 0 9528 8004
# in func 8 9596 8004
# in func 1 8236 8004
# in func 5 10216 8004
# in func 9 8552 8004
# 主进程执行完毕
is_alive:检测进程是否在执行任务.执行的话返回Ture,
import os
from multiprocessing import Process
def func(num):
print('in func',num,os.getpid(),os.getppid())
if __name__ == '__main__':
print('in main',os.getpid(),os.getppid())
p1 = Process(target=func,args=(1,)) # 进程对象
p1.start() # 向操作系统提交了一个开启子进程的申请
print(p1.is_alive()) # 检测进程是否在执行任务
print('主进程执行完了')
terminate:强制结束子进程-非阻塞,
因为将子进程杀死,所以是非阻塞
import os
from time import sleep
from multiprocessing import Process
def func(num):
time.sleep(2)
print('in func',num,os.getpid(),os.getppid())
if __name__ == '__main__':
print('in main',os.getpid(),os.getppid())
p1 = Process(target=func,args=(1,)) # 进程对象
p1.start() # 向操作系统提交了一个开启子进程的申请
print(p1.is_alive()) # 检测进程是否在执行任务
p1.terminate() # 强制结束子进程 - 非阻塞
# sleep(0.5) #解注后,就是Flase
print(p1.is_alive()) # 检测进程是否在执行任务
print('主进程 的 代码执行结束了')
输出:
#in main 9528 1848
#True
#True
#主进程 的 代码执行结束了
因为立刻结束子进程后,并来不及反应处理,sleep解注后就是Flase
用面向对象的方式开启子进程
import os
from multiprocessing import Process
class MyProcess(Process):
def __init__(self,num):
super().__init__()
self.num = num
def run(self):
print('in run ',self.num,os.getpid(),os.getppid())
if __name__ == '__main__':
print('in main ', os.getpid(), os.getppid())
p = MyProcess(1)
p.start()
run函数就是之前写的func函数,
自己定义的类必须继承Process类,
必须写一个run方法(不能改名字),
传参的顺序,赋值给self的.