http://blog.youkuaiyun.com/inte_sleeper/article/details/6741963
Python 实现线程式编程非常简单,但是这种方法的一个缺陷就是它并不总是能够提高应用程序的速度,因为全局解释器锁(Global Interpreter Lock,GIL)将线程有效地限制到一个核中。如果需要使用计算机中的所有核,那么通常都需通过 对 经常使用 fork 操作来实现,从而提高速度。处理进程组是件困难的事情,因为为了在进程之间进行通信,需要对所有调用进行协调,这通常会使事情变得更复杂。
幸运的是,自 2.6 版本起,Python 包括了一个名为 “多进程(multiprocessing)” 的模块来帮助处理进程。该进程模块的 API 与线程 API 的工作方式有些相似点,但是也存在一些需要特别注意的不同之处。主要区别之一就是进程拥有的一些微妙的底层行为,这是高级 API 永远无法完全抽象出来的。可以从多进程模块的官方文档中了解有关这方面内容(参见 参考资料 小节)。
进程和线程在并发性的工作原理方面存在一些明显的差异。通过阅读我撰写的有关线程的 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竟然是2.4额,尼玛,multiprocessing是2.6才引入的~
其实多线程和多进程在实现这个任务上的编码基本一致
- run = os.system
- def proc(node, cmd):
- time.sleep(3)
- print '%d process start' %node
- print cmd
- #run(cmd)
- if __name__ == "__main__":
- print 'controller start.', time.strftime('%Y-%m-%d-%H-%M-%S')
- plist = []
- for i in range(27):
- sfx = '%.2d' %i
- cmd = './filterDep data/2k_6 rst/page.'+sfx+'.pos.fix work/page.' + sfx + '.oracle'
- p = threading.Thread(target = proc, args = (i, cmd))
- plist.append(p)
- p.start()
- print 'controller wait for submuliproc'
- for p in plist:
- p.join()
- print 'controller finish. ',time.strftime('%Y-%m-%d-%H-%M-%S')
- import sys
- import os
- import re
- import multiprocessing
- def run(cmdline):
- print "execute : %s" %cmdline
- #os.system(cmdline)
- print " execute end \n"
- def worker(file):
- cmd = "exe "+file
- run(cmd)
- if __name__ == "__main__":
- num = 7
- pool = multiprocessing.Pool(num)
- for i in range(0,num):
- sfx = '%.2d' %i
- pool.apply_async(worker, ('content.'+sfx,))
- pool.close()
- pool.join()
python标准库中的 Queue.Queue()对多线程是安全的,但是对多进程是非进程安全的
python 多进程实现了进程安全版本的Queue
multiprocessing.Queue()
如果使用多进程要用这个Queue