目录



  • Python如何使用多进程
  • 1.导入multiprocessing中的Process类,并且实例化这个类,指定要执行的任务(如函数)target
  • 2.导入multiprocessing 中的Process类,继承这个类,覆盖run方法,将要执行的任务放入run中,开启进程时则会自动执行该函数
  • 进程之间相互隔离
  • join函数
  • 进程对象的常用属性(了解)
  • 僵尸进程与孤儿进程(了解)



Python如何使用多进程

  • 创建子进程的方式

1.导入multiprocessing中的Process类,并且实例化这个类,指定要执行的任务(如函数)target

import os
from multiprocessing import Process

# Process就表示进程,在很多地方都是这样

def task():
    print('this is sub process')
    print(f'sub process id {os.getpid()}')
    
# 注意,开启进程的代码必须放在__main__判断下面,不然会循环导入,下面会详细解释
if __name__ == '__main__':
    # 实例化一个进程对象
    p = Process(target=task)
    p.start() # 给操作系统发送指令,让它开启进程
    print('this is parent process')
    print(f'parent process is {os.getpid()}')
import os
from multiprocessing import Process

# Process就表示进程,在很多地方都是这样

def task():
    print('this is sub process')
    print(f'sub process id {os.getpid()}')
    
# 注意,开启进程的代码必须放在__main__判断下面,不然会循环导入,下面会详细解释
if __name__ == '__main__':
    # 实例化一个进程对象
    p = Process(target=task)
    p.start() # 给操作系统发送指令,让它开启进程
    print('this is parent process')
    print(f'parent process is {os.getpid()}')
this is parent process
parent process is 2060
this is sub process
sub process id 10008
为什么要写在main下面

linux 与 windows 开启进程的方式不同

linux 会将父进程的内存数据完整copy一份给子进程

windows 会导入父进程的代码,并且从头执行一遍,来获取需要处理的任务

所以在编写代码时一定要将开启进程的代码放在main判断中,否则就会循环导入

2.导入multiprocessing 中的Process类,继承这个类,覆盖run方法,将要执行的任务放入run中,开启进程时则会自动执行该函数

  • 在需要对进程对象进行高度自定义时使用
from multiprocessing import Process
import os

class Downloader(Process):
    
    # def __init__(self, url, size, name):
    #   super().__init__()
    #   self.url = url
    #   self.size = size
    #   self.name = name
    
    def run(self):
        print(os.getpid())
        
if __name__ == '__main__':
    m = Downloader()
    m.start()   # 这里不要使用run(),因为那样只是运行方法而不是开启进程
    print('parent over', os.getpid())
from multiprocessing import Process
import os

class Downloader(Process):
    
    # def __init__(self, url, size, name):
    #   super().__init__()
    #   self.url = url
    #   self.size = size
    #   self.name = name
    
    def run(self):
        print(os.getpid())
        
if __name__ == '__main__':
    m = Downloader()
    m.start()   # 这里不要使用run(),因为那样只是运行方法而不是开启进程
    print('parent over', os.getpid())
parent over 1756    # 父进程打印的
parent over 4048    # 子进程打印的
4048                # 子进程的run()中打印的

从这个例子的打印顺序中可以得知:开启子进程需要时间,所以会先执行父进程

进程之间相互隔离

1.可以在开启进程后使用sleep方法,但是这样做并不好,因为你无法确定这个子进程到底会运行多久

join函数

from multiprocessing import Process

def task1(name):
    for i in range(100):
        print(f'{name} run')
        
def task2(name):
    for i in range(100):
        print(f'{name} run')
        
if __name__ == '__main__':     # args 是给子进程传参的,必须是元祖
    p1 = Process(target=task1, args=('p1',))
    p1.start()
    
    p2 = Process(target=task2, args=('p2',))
    p2.start()
    
    p2.join()   # 让主进程,等待子进程执行完毕后再继续执行
    p1.join()
    
    # 达到的效果是,保证两个子进程是并发执行的,并且over一定是在最后打印
    print('over')
from multiprocessing import Process

def task1(name):
    for i in range(100):
        print(f'{name} run')
        
def task2(name):
    for i in range(100):
        print(f'{name} run')
        
if __name__ == '__main__':     # args 是给子进程传参的,必须是元祖
    p1 = Process(target=task1, args=('p1',))
    p1.start()
    
    p2 = Process(target=task2, args=('p2',))
    p2.start()
    
    p2.join()   # 让主进程,等待子进程执行完毕后再继续执行
    p1.join()
    
    # 达到的效果是,保证两个子进程是并发执行的,并且over一定是在最后打印
    print('over')

案例:

from multiprocessing import Process

def task1(name):
    for i in range(10):
        print(f'{name} run')
        
if __name__ == '__main__':
    ps=[]   # 需要把每一个进程添加到列表中
    for i in range(10):
        p = Process(target=task1, args=(i,))
        p.start()
        ps.append(p)
        
    for i in ps:    # 遍历列表,保证每一个子进程都已经结束了
        i.join()
        
    # 达到的效果是,把100个打印,分给10个子进程去做,最后打印over   
    print('over')
from multiprocessing import Process

def task1(name):
    for i in range(10):
        print(f'{name} run')
        
if __name__ == '__main__':
    ps=[]   # 需要把每一个进程添加到列表中
    for i in range(10):
        p = Process(target=task1, args=(i,))
        p.start()
        ps.append(p)
        
    for i in ps:    # 遍历列表,保证每一个子进程都已经结束了
        i.join()
        
    # 达到的效果是,把100个打印,分给10个子进程去做,最后打印over   
    print('over')

进程对象的常用属性(了解)

方法名

作用

p.name

打印进程名,可以在实例化时添加name参数

p.daemon

守护进程

p.exitcode

获取进程的退出码(就是exit()函数中传入的值)

p.is_alive()

查看进程是否存活

p.pid()

获取进程id(子进程)

p.terminate()

终止进程,与start相同,不会立即终极,操作系统需要时间

僵尸进程与孤儿进程(了解)

孤儿进程 当父进程已经结束,而子进程还在运行,子进程就称为孤儿进程,比如用QQ打开了浏览器,再退掉QQ,那么浏览器就是孤儿进程,这个有其存在的必要性,没有不良影响,孤儿进程会被操作系统所接管

僵尸进程 当一个进程已经结束了,但是它仍然还有一些数据存在,此时称之为僵尸进程

在linux中,有这么一个机制,父进程无论什么时候都可以获取到子进程的一些数据

子进程任务执行完毕后,确实结束了但是仍然保留一些数据,目的是为了让父进程能够获取信息

linux中,可以调用waitpid来彻底释放子进程的残留信息