Python程序中的进程操作-开启多进程
之前我们已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,刚刚我们已经了解了,运行中的程序就是一个进程。所有的进程都是通过它的父进程来创建的。因此,运行起来的python程序也是一个进程,那么我们也可以在程序中再创建进程。多个进程可以实现并发效果,也就是说,当我们的程序中存在多个进程的时候,在某些时候,就会让程序的执行速度变快。以我们之前所学的知识,并不能实现创建进程这个功能,所以我们就需要借助python中强大的模块。
1.mulitprocess模块
仔细说来,mulitprocess不是一个模块而是Python中的一个操作、管理进程的包。之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便大家归类记忆,我讲这部分大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。
2.mulitprocess.process模块
process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。
3.process模块介绍
Process([group [, target [, name [, args [, kwargs]]]]])
,由该类实例化得到的对象,。表示一个子进程中的任务(尚未启动)
强调:
- 需要使用关键字的方式来指定参数
- args指定的未传给target函数的位置参数,是一个元组形式,必须由逗号
参数介绍:
- group参数未使用,值始终未None
- target表示调用对象,即子进程要执行的任务
- args表示调用对象的位置参数元组,
args=(1,2,'egon',)
- kwargs表示调用对象的字典,
kwargs={'name':'egon','age':18}
- name为子进程的名称
3.1方法介绍
p.start()
:启动进程,并调用该子进程的p.run()p.run()
:进程启动时运行的方法,正是它取调用target指定的函数,外我们自定义类的类中一定要实现该方法p.terminate()
:强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁。p.is_alive()
:如果p任然运行,返回Truep.join([timeout])
:主线程等待p终止(强调:时主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
3.2属性介绍
p.daemon
:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()
之前设置p.name
:进程的名称p.pid
:进程的pidp.exitcode
:进程在运行时为None、如果为-N,表示被信号N结束(了解即可)p.authkey
:进程的身份验证键,默认是由os.urandom()
随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
3.3在Windows中注意事项
在Windows操作系统中由于没有fork(Linux操作系统中创建进程的机制),在创建 子进程的是时候会自动import启动它的这个文件,而在import的时候又执行了整个文件。因此如果讲process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ =='__main__'
判断保护起来,import的时候,就不会递归运行了。
4.使用process模块创建进程
在一个python进程中开启子进程,start方法和并发效果。
4.1启动的进程两种方式
#一:定义进程函数
from multiprocessing import Process
import time
def task(name):
print(f'{name} is running')
time.sleep(2)
print(f'{name} is over')
if __name__ == '__main__':
# 1 创建一个对象
p = Process(target=task, args=('qq',))
# 开启进程
p.start() # 告诉操作系统让其创建一个进程,这个进程是异步的
# time.sleep(1)
print('主')
# 二:类的继承
from multiprocessing import Process
import time
class MyProcess(Process):
def run(self):
print('hello')
time.sleep(2)
print('bye')
if __name__ == '__main__':
p = MyProcess()
p.start()
# time.sleep(1)
print('主')
总结:
-
创建进程就是在内存中申请一块内存空间将需要运行的代码丢进去
-
一个进程对应在内存中就是一块独立的内存空间
-
多个进程对应在内存中就是多块独立的内存空间
-
进程与进程之间数据默认情况下是无法直接交互,如果想交互可以借助于第三方工具、模块
4.2join方法
join是让主进程等待子进程代码运行结束之后,再继续运行。不影响其他子进程的执行
from multiprocessing import Process
import time
def task(name, n):
print('%s is running'%name)
time.sleep(n)
print('%s is over'%name)
if __name__ == '__main__':
# p1 = Process(target=task, args=('jason', 1))
# p2 = Process(target=task, args=('egon', 2))
# p3 = Process(target=task, args=('tank', 3))
# start_time = time.time()
# p1.start()
# p2.start()
# p3.start() # 仅仅是告诉操作系统要创建进程
# # time.sleep(50000000000000000000)
# # p.join() # 主进程等待子进程p运行结束之后再继续往后执行
# p1.join()
# p2.join()
# p3.join()
start_time = time.time()
p_list = []
for i in range(1, 4):
p = Process(target=task, args=('子进程%s'%i, i))
p.start()
p_list.append(p)
for p in p_list:
p.join()
print('主', time.time() - start_time)
4.3进程之间数据相互隔离
from multiprocessing import Process
money = 100
def task():
global money # 局部修改全局
money = 666
print('子',money)
if __name__ == '__main__':
p = Process(target=task)
p.start()
p.join()
print(money)
"""
子 666
100
"""
4.4进程对象以及其他方法
计算机会给各个运行的进程分配进程进程控制符PID,也可以称之为进程号( Process Identifier),用于区分和管理各个进程
如果查看: windows电脑 进入cmd输入tasklist即可查看
tasklist |findstr PID 查看具体的进程;mac电脑 进入终端之后输入ps aux,ps aux|grep PID查看具体的进程 。
from multiprocessing import Process, current_process
import time
import os
# def task():
# # print('%s is running'%current_process().pid) # 查看当前进程的进程号
# print('%s is running'%os.getpid()) # 查看当前进程的进程号
# # print('子进程的主进程号%s'%os.getppid()) # 查看当前进程的进程号
# time.sleep(30)
def task():
# 查看当前进程的进程号
print(f'{current_process().pid} is running')
# 用os模块的方法查看进程号
print(f'{os.getpid()}')
# 用os模块的方法查看当前进程的父进程号
print(f'{os.getppid()}')
time.sleep(3)
if __name__ == '__main__':
p = Process(target=task)
# 启动进程
p.start()
# 杀死当前进程
# ps: 只是告诉了操作系统去杀死当前进程,
# 但是这个操作需要一定的时间,然后代码的运行速度是极快的
# p.terminate()
# 因为代码运行速度极快,进程还没有真正的杀死掉,
# 打印当前进程存活状态为True
# print(p.is_alive()) # 判断进程是否存活
print('主')
ps:一般情况下我们会默认的将布尔值的变量名和返回布尔值的方法名以is_这种形式开头
4.5僵尸进程与孤儿进程
- 僵尸进程:
通俗来讲就是程序死了但没有完全死透,没有死透意思是说进程死后进程所占的进程号不会立即释放。
- 原因:
是为了让父进程能够查看到自己子进程的信息,比如说:占用的PID号,开启的时间,运行时间,终止的时间...
- 会出现的问题:
所有的进程终止后多会步入僵尸进程,如果父进程不死并且无限制的造子进程,这个时候子进程又不结束,这种情况会严重的消耗计算机资源
- 僵尸进程PID回收办法:
-
父进程等待子进程运行结束
-
父进程调用join方法
import time
def run():
print('hello world')
time.sleep(1)
print('get out')
if __name__ == '__main__':
p = Process(target=run)
p.start()
print('主')
'''
主
hello world
get out'''
- 孤儿进程:
创建子进程的父进程意外死亡,而子进程还存活,那么这个子进程就是孤儿进程,操作系统会开始init进程所收养孤儿进程,由init进程对它们完成状态的收集工作。
4.6守护进程
被守护进程结束,守护进程跟着结束
def task(name):
print(f'{name}正在上班')
time.sleep(2)
print(f'{name}下班了')
if __name__ == '__main__':
p=Process(target=task,args=('tom',))
p.start()
print('公司没了')
'''
公司没了
tom正在上班
tom下班了'''
def task(name):
print(f'{name}正在上班')
time.sleep(2)
print(f'{name}下班了')
if __name__ == '__main__':
p=Process(target=task,args=('tom',))
# 将进程p设置成守护进程
# 这一句一定要放在start方法上面才有效否则会直接报错
p.daemon=True
p.start()
print('公司没了')
'''
公司没了
'''