开进程的过程是怎样的呢?下面来看看例子:
from multiprocessing import Process
import time
def task(name):
print(f'{name} is running')
time.sleep(2)
print(f'{name} finish')
if __name__ == '__main__':
p = Process(target=task, args = ('andy',))
p.start()
print('主进程')
#输出
主进程
andy is running
andy finish
当代码执行到p.start时,主进程向系统发送一个通知,创建一个子进程,但创建进程开销比较大,主进程并不会等待它成,而是接着向下执行,而子进程会将主进程的初始数据(p.start()之前的数据)复制一份。
子进程必须依赖主进程, 子进程会复制子进程的数据,开进程开销比较大。所以永远先执行主进程的代码。
我们略修改下代码:
import time
from multiprocessing import Process
def task(name):
print(f'{name} is running')
time.sleep(2)
print(f'{name} finish')
if __name__ == '__main__':
p = Process(target=task, args=('andy',))
p.start()
print('主进程')
time.sleep(3)
print('主结束')
#输出
主进程
andy is running
andy finish
主结束
可以看到,在主进程执行过程中,子进程已经准备好了,所以就又执行了子进程,然后接着执行你进程。
第二种开启进程的方法:
class MyProcess(Process):
def run(self):
print(f'{self.name} is running')
time.sleep(2)
print(f'{self.name} finish')
if __name__ == '__main__':
p = MyProcess()
p.start()
print('主')
必须定义run方法。
获取 进程id: os.getpid()
获取父进程id: os.getppid()
父子进程之间的空间隔离:
当父进程创建子进程时,会将父进程的初始数据复制一份,但它们是数据是相互隔离的。看下面的例子:
lst = ['andy',]
def task():
lst.append('jack')
print(f'子进程{lst}')
if __name__ == '__main__':
p = Process(target=task, )
p.start()
time.sleep(2)
print(f'主{lst}')
#输出:
子进程['andy', 'jack']
主['andy']
所以,可以看出这里的复制是deep.copy(),即空间隔离的,互相不影响,复制只是复制了初始数据。
join
def task(sec):
print(f'子进程{os.getpid()} start')
time.sleep(sec)
print(f'{os.getpid()} finish')
if __name__ == '__main__':
start_time = time.time()
p1 = Process(target=task, args=(2,))
p2 = Process(target=task, args=(3,))
p3 = Process(target=task, args=(5,))
p1.start()
p2.start()
p3.start()
p1.join()
# join只针对 主进程 告诉进程,等我子进程执行完了,你再执行
print(f'p1 主{time.time() - start_time}')
p2.join()
print(f'p2 主{time.time() - start_time}')
p3.join()
print(f'p3主{time.time() - start_time}')
print(f'主{os.getpid(), (time.time() - start_time)} ')
# 如果有多次join, join之间是不阻塞的,同时执行
# 输出
子进程2706 start
子进程2707 start
子进程2708 start
2706 finish
p1 主2.031641960144043
2707 finish
p2 主3.0549099445343018
2708 finish
p3主5.0787928104400635
主(2705, 5.078842878341675)
可以看到,join虽然 告诉主进程,等我子进程执行完了,你再执行,但三多个Join之间是不阻塞的,是同时进行的。
守护进程:
守护进程就是只要主进程结束了,守护进程就结束,类似于皇帝死了,太监就陪葬了
def task(sec):
print(f'子进程{os.getpid()} start')
time.sleep(sec)
print(f'{os.getpid()} finish')
if __name__ == '__main__':
p1 = Process(target=task, args=(2,))
p1.daemon = True
p1.start()
print('主进程')
#输出
主进程
我们稍作修改:
def task(sec):
print(f'子进程{os.getpid()} start')
time.sleep(sec)
print(f'{os.getpid()} finish')
if __name__ == '__main__':
p1 = Process(target=task, args=(2,))
p1.daemon = True # 必须在start之前开启
p1.start()
time.sleep(1)
print('主进程')
#输出
子进程2718 start
主进程
可以看到,主进程在睡了一秒的时间内,子进程执行了一部分,随后主进程结束了,子进程跟着就结束了。导致子进程部分没有执行完。