python学习笔记(3)-进程和线程(一)-多进程


一. 多进程

1. 要让python实现多进程,首先了解操作系统的知识。
Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(父进程)复制了一份(子进程)。然后分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID,这样做的理由是,一个父进程可以fork出很多子进程,所以父进程要记住每个子进程的ID,而子进程只要调用getppid() 就可以拿到父进程的ID。
python 的os模块封装了常见的操作系统调用,其中就包括fork(),就可以在python程序中创建子进程。
示例:

#!/bin/bash
import os
print 'process (%s) start...' %os.getpid()
pid  = os.fork()
if pid == 0:
        print 'I am child process (%s) and my parent is %s' %(os.getpid(),os.getppid())
else:
        print 'I (%s) just create a child process (%s)'  %(os.getpid(),pid)

运行结果:

[root@master python]# python demo8.py 
process (27743) start...
I (27743) just create a child process (27744)
I am child process (27744) and my parent is 27743

有了fork调用,一个进程在接到新的任务时就会复制出一个子进程来处理新任务,常见的apache服务器就是由父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求。

2. multiprocessing跨平台版本的多进程模块。
multiprocessing 模块提供了一个process类代表一个进程对象。下面的例子演示了启动一个子进程并等待结束。

#!/bin/bash
from multiprocessing import Process
import os
def run_proc(name):
        print 'Run child process %s (%s) ...' %(name,os.getpid())
if __name__ ==  '__main__':
        print 'Parent process %s ' %os.getpid()
        p = Process(target = run_proc,args = ('test',))
        print 'process will start'
        p.start()
        p.join()
        print 'Process end'

运行结果;
[root@master python]# python demo9.py 
Parent process 28125 
process will start
Run child process test (28126) ...
Process end

创建一个子进程,只需要传入一个执行函数和函数的参数,创建一个Process 实例,用start()方法启动,   这样 创建进程比fork() 还要简单。join() 方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。

3. Pool进程池
如果需要大量启动子进程,可以使用进程池的方式批量创建子进程:
from multiprocessing import Pool
import os,time,random
def long_time_task(name):
        print 'Run task %s (%s) ...' % (name,os.getpid())
        start = time.time()
        time.sleep(random.random()*3)
        end = time.time()
        print 'task %s run %0.2f seconds' %(name,(end-start))

if __name__ == '__main__':
        print 'Parent process %s' %os.getpid()
        p = Pool()
        for i in range(5):
                p.apply_async(long_time_task,(i,))
        print 'Waiting for all subprocesses done... '
        p.close()
        p.join()
        print 'All subprocesses done.'

运行结果:

[root@master python]# python demo10.py
Parent process 28498
Waiting for all subprocesses done... 
Run task 0 (28499) ...
Run task 1 (28500) ...
task 1 run 0.14 seconds
Run task 2 (28500) ...
task 2 run 0.43 seconds
Run task 3 (28500) ...
task 3 run 1.46 seconds
Run task 4 (28500) ...
task 4 run 0.58 seconds
task 0 run 2.79 seconds
All subprocesses done.

对Pool对象调用join() 方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),  调用close() 之后就不能继续添加吸毒呢Process() 了。
对输出的结果,task1234是立即执行的,而task4 要等前面的某个task完成后才能执行,这是因为Pool的默认大小在我的电脑上是4,默认是CPU的核数。
 p.apply_async() 是向进程池提交目标请求,join() 用来等待进程池中的worker进程执行完毕,防止主进程在worker进程结束前结束。

4. 进程间通信
python 的multiprocessing 模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

示例:在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue中读数据。
#!/bin/bash
from multiprocessing import Process, Queue
import os, time,random
def write(q):
        for value in ['A','B','C']:
                print 'put %s into queue.' %value
                q.put(value)
                time.sleep(random.random())
def read(q):
        while True:
                value = q.get(True)
                print 'get %s from Queue...' %value
if __name__ == '__main__':
        q = Queue()
        pw = Process(target = write,args = (q,))
        pr = Process(target = read,args = (q,))
        pw.start()
        pr.start()
        pw.join()
        pr.terminate()

运行结果:
put A into queue.
get A from Queue...
put B into queue.
get B from Queue...
put C into queue.
get C from Queue...

在Unix/Linux 下multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节,由于windows没有fork()调用,因此,父进程所有的python对象都必须通过pickle序列化再传到子进程去。

总结:  

在Unix/Linux下,可以使用fork()调用实现多进程。

要实现跨平台的多进程,可以使用multiprocessing模块。

进程间通信是通过QueuePipes等实现的。


close() 跟terminate()的区别在于close()会等待池中的worker进程执行结束后再关闭pool,而terminate()是直接关闭。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值