python多进程程序设计 之一
multiprocessing
multiprocessing 是一个python模块,它使用类似于线程模块的 API生成进程。multiprocessing模块提供本地和远程并行运行,通过使用子进程代替线程,有效地回避python全局解释器锁。因此,多处理模块允许程序员充分利用给定机器上的多个处理器。
multiprocessing模块还引入了线程模块中没有API。一个典型的例子是 Pool 对象,它提供了一种方便的方法,跨多个输入值并行执行函数,跨进程分配输入数据(数据并行性)。
Process类
在 multiprocessing中,通过创建Process对象,然后调用其 start() 方法来生成进程。
产生进程
根据平台的不同,多处理支持三种启动进程的方式。这些启动方法是
- spawn, 父进程启动一个新的Python解释器进程。子进程只会继承运行进程对象的 run() 方法所需的资源。特别是,来自父进程的,不必要的文件描述符和句柄将不会被继承。与使用 fork 或 forkserver 相比,使用此方法启动进程相当慢。
- fork, 父进程使用 os.fork() 来 fork Python 解释器。子进程在启动时实际上与父进程相同。父进程的所有资源都被子进程继承。
- forkserver, 当程序选择 forkserver 启动方法时,会生成一个服务器进程。从那时起,每当需要新进程时,父进程就会连接到服务器并请求它派生一个新进程。 fork 服务器进程是单线程的,除非系统库或预加载的imports产生线程的副作用,因此使用 os.fork() 通常是安全的。不会继承不必要的资源。
在 POSIX 上,使用spawn或forkserver启动方法还将启动一个资源跟踪器进程,该进程跟踪由程序进程创建的未链接的命名系统资源(例如,命名信号量或SharedMemory对象)。当所有进程退出后,资源跟踪器将取消任何剩余跟踪对象的链接。通常应该没有这样的对象,但如果进程被信号杀死,则可能会出现一些"泄漏"资源。
set_start_method()启动
在主模块的 if name == 'main' 子句中,使用 set_start_method() 。
from multiprocessing import *
def subproc():
print("subprocess")
if __name__ == '__main__':
set_start_method('spawn')
p = Process(target=subproc)
p.start()
p.join()
显示器输出
javascript
subprocess
get_context()启动
使用 get_context() 来获取上下文对象。上下文对象具有与multiprocessing模块相同的 API,并允许在同一程序中多次使用启动方法。
from multiprocessing import *
def subproc():
print("subprocess")
if __name__ == '__main__':
print("call get_context...")
ctx = get_context('spawn')
p = ctx.Process(target=subproc)
p.start()
p.join()
显示器输出
javascript
call get_context...
subprocess
成员函数
构造函数
multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
Process对象表示在单独流程中运行的活动。
- group,应始终为None。
- target, 是由 run() 方法调用的可调用对象。它默认为 None,这意味着什么都不被调用。
- name, 是进程名称。
- args,是目标调用的参数元组。
- kwargs, 是目标调用的关键字参数的字典。
- daemon,如果提供,daemon参数将进程的daemon标志设置为 True 或 False。如果 None (默认),该标志将从创建进程继承。
默认情况下,没有参数传递给target。 args 参数默认为 (),可用于指定要传递给target的参数列表或元组。
如果子类重写构造函数,则它必须确保,在对进程执行任何其他操作之前,调用基类构造函数 (Process.init())。
run/start
词法: run()
Process类的成员函数run()表示进程的活动。
在子类中可以重载这个函数。标准run()函数调用可调用对象target,target是作为参数target传递给构造函数。如果该构造函数包含可调用对象target,则构造器的参数args 和 kwargs,将作为参数,传递给可调用对象target。
词法:start()
Process类的成员函数start()启动进程的活动。
每个进程对象最多只能调用一次。它在单独的进程中,调用对象的 run() 方法。
join
词法: join([timeout])
- 如果可选参数 timeout为None (默认值),则该函数将阻塞,直到调用 join() 函数的进程终止。
- 如果 timeout 是正数,则最多阻塞 timeout 秒。如果该函数的进程终止,或该函数超时,则该函数将返回 None。检查进程的退出代码,以确定它是否终止。
一个进程可以多次调用join。
进程无法join自身,因为这会导致死锁。在进程启动之前,调用join是错误的。
应用实列
实列说明
该实列使用两种不同的方法产生进程的活动
- Consumer子进程使用构造函数
- Producer子进程使用进程子类run()成员函数。在__init__(self,...)中,必须调用super(Producer,self).init()初始化基类的初始化函数。
- Producer子进程使用进程子类的参数传递方法更方便,灵活。
实列代码
from multiprocessing import *
def Consumer(v1, v2, v3, width):
print("Consumer v1:{0} v2:{1} v3: {2} width: {3}".format(v1, v2, v3, width))
class Producer(Process):
def __init__(self, args, keys):
super(Producer,self).__init__()
self.args = args;
self.keys = keys;
def run(self):
print("Producer args: {0}".format(self.args))
print("Producer keys: {0}".format(self.keys))
if __name__ == '__main__':
print("call get_context...")
ctx = get_context('spawn')
args = [1, 2, 3]
keys = {"width":"12"}
p1 = ctx.Process(target=Consumer, args=args, kwargs=keys)
p2 = Producer(args, keys);
p1.start()
p2.start()
p1.join()
p2.join()
print("Consumer exit code:{0}".format(p1.exitcode))
print("Producer exit code:{0}".format(p2.exitcode))
显示器输出
call get_context...
Consumer v1:1 v2:2 v3: 3 width: 12
Producer args: [1, 2, 3]
Producer keys: {'width': '12'}
Consumer exit code:0
Producer exit code:0