python多进程编程
2011-09-02 10:30
381 查看
部分转载自:http://www.ibm.com/developerworks/cn/aix/library/au-multiprocessing/
Python 实现线程式编程非常简单,但是这种方法的一个缺陷就是它并不总是能够提高应用程序的速度,因为全局解释器锁(Global Interpreter Lock,GIL)将线程有效地限制到一个核中。如果需要使用计算机中的所有核,那么通常都需通过 对 经常使用 fork 操作来实现,从而提高速度。处理进程组是件困难的事情,因为为了在进程之间进行通信,需要对所有调用进行协调,这通常会使事情变得更复杂。
幸运的是,自 2.6 版本起,Python 包括了一个名为 “多进程(multiprocessing)” 的模块来帮助处理进程。该进程模块的 API 与线程 API 的工作方式有些相似点,但是也存在一些需要特别注意的不同之处。主要区别之一就是进程拥有的一些微妙的底层行为,这是高级 API 永远无法完全抽象出来的。可以从多进程模块的官方文档中了解有关这方面内容(参见
参考资料 小节)。
fork 简介
进程和线程在并发性的工作原理方面存在一些明显的差异。通过阅读我撰写的有关线程的 developerWorks 文章,可以进一步了解这些差异(参见
参考资料)。在进程执行 fork 时,操作系统将创建具有新进程 ID 的新的子进程,复制父进程的状态(内存、环境变量等)。首先,在我们实际使用进程模块之前,先看一下 Python 中的一个非常基本的 fork 操作。
现在来看一下以上代码的输出:
在下一个示例中,增强初始 fork 的代码,并设置一个环境变量。该环境变量随后将被复制到子进程中。下面给出了相应的代码:
下面给出了 fork 的输出:
在输出中,可以看到 “修改后的” 环境变量 FOO 留在了子进程和父进程中。您可以通过在父进程中再次修改环境变量来进一步测试这个示例,您将看到子进程现在是完全独立的,它有了自己的生命。注意,子进程模块也可用于 fork 进程,但是实现方式没有多进程模块那么复杂。
多进程简介
现在您已经了解 Python fork 操作的基本知识,让我们通过一个简单例子了解它在更高级的多进程库中的使用。在这个示例中,仍然会出现 fork,但是已经为我们处理了大部分标准工作。
如果查看输出,将会看到下面的内容:
可以看到从主进程分出了一个子进程,该子进程随后休眠了 5 秒种。子进程分配是在调用
上面的代码实现了一个多进程执行命令的控制器,它根据获取的节点参数,创建多个进程,ssh到节点上并执行命令。
进程实际上是在p.start的时候真正创建并且开始运行的。调用p.join就跟线程一样,会等待进程结束。
Python 实现线程式编程非常简单,但是这种方法的一个缺陷就是它并不总是能够提高应用程序的速度,因为全局解释器锁(Global Interpreter Lock,GIL)将线程有效地限制到一个核中。如果需要使用计算机中的所有核,那么通常都需通过 对 经常使用 fork 操作来实现,从而提高速度。处理进程组是件困难的事情,因为为了在进程之间进行通信,需要对所有调用进行协调,这通常会使事情变得更复杂。
幸运的是,自 2.6 版本起,Python 包括了一个名为 “多进程(multiprocessing)” 的模块来帮助处理进程。该进程模块的 API 与线程 API 的工作方式有些相似点,但是也存在一些需要特别注意的不同之处。主要区别之一就是进程拥有的一些微妙的底层行为,这是高级 API 永远无法完全抽象出来的。可以从多进程模块的官方文档中了解有关这方面内容(参见
参考资料 小节)。
fork 简介
进程和线程在并发性的工作原理方面存在一些明显的差异。通过阅读我撰写的有关线程的 developerWorks 文章,可以进一步了解这些差异(参见
参考资料)。在进程执行 fork 时,操作系统将创建具有新进程 ID 的新的子进程,复制父进程的状态(内存、环境变量等)。首先,在我们实际使用进程模块之前,先看一下 Python 中的一个非常基本的 fork 操作。
#!/usr/bin/env python """A basic fork in action""" import os def my_fork(): child_pid = os.fork() if child_pid == 0: print "Child Process: PID# %s" % os.getpid() else: print "Parent Process: PID# %s" % os.getpid() if __name__ == "__main__": my_fork()
现在来看一下以上代码的输出:
mac% python fork.py Parent Process: PID# 5285 Child Process: PID# 5286
在下一个示例中,增强初始 fork 的代码,并设置一个环境变量。该环境变量随后将被复制到子进程中。下面给出了相应的代码:
#!/usr/bin/env python """A fork that demonstrates a copied environment""" import os from os import environ def my_fork(): environ['FOO']="baz" print "FOO environmental variable set to: %s" % environ['FOO'] environ['FOO']="bar" print "FOO environmental variable changed to: %s" % environ['FOO'] child_pid = os.fork() if child_pid == 0: print "Child Process: PID# %s" % os.getpid() print "Child FOO environmental variable == %s" % environ['FOO'] else: print "Parent Process: PID# %s" % os.getpid() print "Parent FOO environmental variable == %s" % environ['FOO'] if __name__ == "__main__": my_fork()
下面给出了 fork 的输出:
mac% python env_fork.py FOO environmental variable set to: baz FOO environmental variable changed to: bar Parent Process: PID# 5333 Parent FOO environmental variable == bar Child Process: PID# 5334 Child FOO environmental variable == bar
在输出中,可以看到 “修改后的” 环境变量 FOO 留在了子进程和父进程中。您可以通过在父进程中再次修改环境变量来进一步测试这个示例,您将看到子进程现在是完全独立的,它有了自己的生命。注意,子进程模块也可用于 fork 进程,但是实现方式没有多进程模块那么复杂。
多进程简介
现在您已经了解 Python fork 操作的基本知识,让我们通过一个简单例子了解它在更高级的多进程库中的使用。在这个示例中,仍然会出现 fork,但是已经为我们处理了大部分标准工作。
#!/usr/bin/env python from multiprocessing import Process import os import time def sleeper(name, seconds): print 'starting child process with id: ', os.getpid() print 'parent process:', os.getppid() print 'sleeping for %s ' % seconds time.sleep(seconds) print "Done sleeping" if __name__ == '__main__': print "in parent process (id %s)" % os.getpid() p = Process(target=sleeper, args=('bob', 5)) p.start() print "in parent process after child process start" print "parent process about to join child process" p.join() print "in parent process after child process join" print "parent process exiting with id ", os.getpid() print "The parent's parent process:", os.getppid()
如果查看输出,将会看到下面的内容:
mac% python simple.py in parent process (id 5245) in parent process after child process start parent process about to join child process starting child process with id: 5246 parent process: 5245 sleeping for 5 Done sleeping in parent process after child process join parent process exiting with id 5245 The parent's parent process: 5231
可以看到从主进程分出了一个子进程,该子进程随后休眠了 5 秒种。子进程分配是在调用
p.start()时发生的。在下一节中,您将看到这个基础的程序将扩展为更大的程序。
#!/usr/bin/python import os, sys, time, datetime, traceback; from multiprocessing import Process; nodes = ('master', 'slave1', 'slave5', 'slave17'); cmd = 'ssh %s "/xxx_project/download/dump_data.sh %s"' def download(node, exec_cmd): try: print "[INFO]:Download started on node:",node; print("[INFO]:" + exec_cmd); if os.system(exec_cmd) != 0: print("[ERROR]:" + exec_cmd); print "[INFO]:Download finished on node:",node; except Exception as e: #table doesn't exist probably print e; traceback.print_exc(); if __name__ == "__main__": print("%s: controller started." % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()),)); date_str = '' if len(sys.argv) > 1: date_str = sys.argv[1]; plist = []; for node in nodes: exec_cmd = cmd % (node, date_str); p = Process(target = download, args = (node, exec_cmd)); plist.append(p); p.start(); for p in plist: p.join(); print("%s: controller finished." % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()),));
上面的代码实现了一个多进程执行命令的控制器,它根据获取的节点参数,创建多个进程,ssh到节点上并执行命令。
进程实际上是在p.start的时候真正创建并且开始运行的。调用p.join就跟线程一样,会等待进程结束。
相关文章推荐
- python多进程编程的一点积累
- Python并发编程之进程
- Python - 多进程编程, 不怕GIL
- python使用multiprocessing进行多进程编程(1)
- Python多进程编程技术实例分析
- 理论讲解python多进程并发编程
- Python并发编程之进程
- 理解Python并发编程一篇就够了 | 进程篇
- Python的多进程编程
- [zz]python多进程编程
- python使用deco进行多进程编程
- [Python网络编程]浅析守护进程后台任务的设计与实现
- Python并发编程之进程
- Python基础(八)-系统编程之进程--multiprocessing(阻塞非阻塞)
- python使用multiprocessing进行多进程编程(1)
- Python多进程编程
- 使用Python进行多进程编程
- 理解python并发编程-进程篇
- Python多进程编程 (Mark-备用)
- Python进程编程小结