Python 进程

开进程的过程是怎样的呢?下面来看看例子:

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
主进程

可以看到,主进程在睡了一秒的时间内,子进程执行了一部分,随后主进程结束了,子进程跟着就结束了。导致子进程部分没有执行完。

上一篇:Python 上下文管理器

下一篇:Python 僵尸进程与孤儿进程