Pyhton中多进程编程方式
os.fork()
Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程,例程如下:
1 | import os |
运行结果如下:
Process (6221) start...
I (6221) just created a child process (6222).
I am child process (6222) and my parent is 6221.
普通的函数调用,调用一次,返回一次,但是fork()
调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()
就可以拿到父进程的ID。
值得注意的是,由于Windows没有fork调用,上面的Python代码在Windows上无法运行。
multiprocessing.Process
multiprocessing模块就是跨平台版本的多进程模块。multiprocessing模块提供了一个Process类来代表一个进程对象。例程如下:
1 | from multiprocessing import Process |
运行结果如下:
Parent process 26656.
Child process will start.
Run child process test (26657)...
Child process end.
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()
方法启动,这样创建进程比fork()
还要简单。join()
方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
multiprocessing.Pool
如果要启动大量的子进程,可以用进程池的方式批量创建子进程。例程如下:
1 | from multiprocessing import Pool |
运行结果如下:
1 | Parent process 669. |
对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。
请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的的大小是4,因此,最多同时执行4个进程。如果Pool(5)那么就可以同时执行5个子进程。
subprocess
subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。下面的例子演示了如何在Python代码中运行命令nslookup www.python.org
,这和命令行直接运行的效果是一样的:
1 | import subprocess |
运行结果如下:
1 | $ nslookup www.python.org |
如果子进程还需要输入,则可以通过communicate()
方法输入,可以做如下修改调整:
1 | import subprocess |
上面的代码相当于在命令行执行命令nslookup,然后手动输入:
set q=mx
python.org
exit
进程间通信
Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:
1 | from multiprocessing import Process, Queue |
运行的结果如下:
1 | Process to write: 50563 |
总结
在Unix/Linux下,可以使用fork()调用实现多进程。要实现跨平台的多进程,可以使用multiprocessing模块。进程间通信是通过Queue、Pipes等实现的。