python 33 多进程(一)

多进程

1. 进程创建的两种方式 multiprocessing

# 第一种方式
from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is gone')

if __name__ == '__main__':   # 在Windows环境下,必须在main下开启子进程
    p = Process(target=task, args=('meet',))   # args元组形式   创建一个进程对象
    p.start()
    print("===主进程")

"""
结果:
===主进程
meet is running
meet is done
"""

# p.start():开启子进程的命令,通知操作系统在内存中开辟一个进程空间,将主进程的数据copy加载到子进程空间中,然后操作系统调用cpu去执行子进程。    开销较大,因此会先执行主进程的代码。
# 第二种方式
from multiprocessing import Process
import time

class MyProcess(Process):
    
    def __init__(self, name):
        super().__init__()		# 重构父类的方法
        self.name = name
    def run(self):			# 必须写run()
        print(f'{self.name} is running')
        time.sleep(2)
        print(f"{self.name} is gone")

if __name__ == '__main__':
    p = MyProcess('meet')	# 默认执行run()
    p.start()		# 开启子进程的命令
    print("===主进程")

2. 进程pid (process id)

进程在内存中的唯一标识。

终端命令: tasklist # 查看计算机当前所有进程的pid

终端命令: tasklist|findstr pycharm # 查看pycharm的pid

代码 :

import os
print(os.getpid())		# 查看当前进程的pid

print(os.getppid())		# 查看父进程的pid

3. 验证进程之间的空间隔离

from multiprocessing import Process

lst = [1, 2]
def task():
    lst.append(3)
    print(f'子进程的:{lst}')

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    print(f'主进程的:{lst}')
"""
主进程的:[1, 2]
子进程的:[1, 2, 3]
"""
# 主进程与子进程之间存在空间隔离。

4. 进程对象join方法

join :让主进程等待子进程结束之后,再执行主进程。(让主进程阻塞) 
from multiprocessing import Process
import time

def task(sec):
    print('is running')
    time.sleep(sec)
    print('is gone')

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()
    print(f'p1时间:{time.time()-start_time}')  # 1s多
    p2.join()
    print(f'p2时间:{time.time()-start_time}')	 # 2s多
    p3.join()
    print(f'p3时间:{time.time()-start_time}')  # 3s多

from multiprocessing import Process
import time

def task(sec):
    print('is running')
    time.sleep(sec)
    print('is gone')

if __name__ == '__main__':
    start_time = time.time()

    p1 = Process(target=task, args=(3,))
    p2 = Process(target=task, args=(2,))
    p3 = Process(target=task, args=(1,))  # 改变时间
    p1.start()
    p2.start()
    p3.start()
# 同时开启
    p1.join()
    print(f'p1时间:{time.time()-start_time}')  # 3s多  #阻塞
    p2.join()
    print(f'p2时间:{time.time()-start_time}')	 # 3s多
    p3.join()
    print(f'p3时间:{time.time()-start_time}')  # 3s多
# 面试题:将下面代码优化
from multiprocessing import Process
import time

def task(sec):
    print('is running')
    time.sleep(sec)
    print('is gone')

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()
    print(f'运行时间:{time.time()-start_time}')  # 3s多
    
# 上面代码优化:
    lst = []
    for i in range(1,4):
        p = Process(target=task, args=(i,))
        lst.append(p)
        p.start()

    for i in lst:
        i.join()
    print(f'运行时间:{time.time()-start_time}')
    
# 错误的优化:
	for i in range(1,4)
       p = Process(target=task, args=(i,))
       p.start()
       p.join()
    print(f'运行时间:{time.time()-start_time}')

5. 进程对象其他属性

p.terminate()		# 结束子进程

p.is_alive()		# 判断子进程是否活着 True/False

p = Process(target=task, args=('meet',), age=18)	# 给子进程对象添加一个age属性。
print(p.age)	# 18

6. 僵尸进程与孤儿进程

6.1 僵尸进程

实际中,主进程只有等待子进程结束后,主进程才结束。

基于Unix环境(linux,macOS):

主进程时刻监测子进程的运行状态,当子进程结束后,一段时间内,才会将子进程回收。

为什么不马上回收?

  1. 主进程与子进程是异步关系,主进程无法马上会获取子进程是什么时候结束;
  2. 如果子进程结束之后马上在内存中释放资源,主进程就没有办法监测子进程的状态。

Unix针对上面的问题,提供了一种机制:

所有的子进程结束后,立马释放掉文件的操作链接、内存等的大部分数据,但是会保留一些内容:进程号、结束时间、运行状态,等待子进程监测、回收。

僵尸进程:子进程在结束后,主进程并没有调用wait或waitpid获取子进程的状态信息,子进程仍然保留了一些描述内容,这种进程被称为僵尸进程。

​ 危害:如果父进程不对僵尸进程进行回收(wait/waitpid),产生大量的僵尸进程,这样会占用内存,占用进程的pid号。

6.2 孤儿进程

父进程由于某种原因结束,但子进程还在运行中,则称这些子进程为孤儿进程。

回收机制:Unix中孤儿进程会被 init 进程回收,init 变成父进程。

僵尸进程解决方法

​ 直接杀死父进程。将所有僵尸进程变成孤儿进程,由 init进程回收。

7. 守护进程

# 子进程守护着主进程,只要主进程结束,子进程也跟着结束。

from multiprocessing import Process
import time

def task(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is gone')

if __name__ == '__main__':
    p = Process(target=task, args=('meet',))   # 创建一个进程对象
    p.daemon = True		# 放在start前,否则会报错。
    p.start()
    time.sleep(1)
    print("===主进程")
"""
meet is running
===主进程
"""
原文地址:https://www.cnblogs.com/yzm1017/p/11390979.html