要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识。

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

 # /usr/bin/python
import os
import time def main():
pid = os.fork()
if pid == 0: # 子进程中返回0
print("I am child process %d, my parent is %d" % (os.getpid(), os.getppid()))
time.sleep(1)
else: # 父进程中返回子进程id
print("I %d just created child %d" % (os.getpid(), pid))
time.sleep(1) # 防止父进程提前结束,子进程将由init进程接管,导致子进程中的os。getppid()输出的进程id是1 if __name__ == '__main__':
main()

程序运行结果:

 I 20981 just created child 20982
I am child process 20982, my parent is 20981

由于Windows没有fork调用,上面的代码在Windows上无法运行。在Linux,Mac,Unix上都可以运行

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

multiprocessing

如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

(1)Process类:

 import multiprocessing
import os
import time # 子进程要执行的代码
def run_process(i):
print("%d Child %s process run" % (i, multiprocessing.current_process().name, ))
time.sleep(1)
print("%d Child %s process end" % (i, multiprocessing.current_process().name,)) def main():
print("Process %d run" % (os.getpid()))
p1 = multiprocessing.Process(target=run_process, args=(1,)) # 和多线程Thread类创建实例相似
p1.start()
p1.join() # 主进程等待子进程结束
print("Process %d stop" % (os.getpid())) if __name__ == '__main__':
main()

执行结果如下:

 Process 22324 run
1 Child Process-1 process run
1 Child Process-1 process end
Process 22324 stop

创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。

join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。

(2)Pool类:

在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量的时间。如果操作的对象数目不大时,还可以直接使用Process类动态的生成多个进程,十几个还好,但是如果上百个甚至更多,那手动去限制进程数量就显得特别的繁琐,此时Pool类就派上用场了。 
Pool类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。

 import multiprocessing
import os
import time def run_process(i):
print("%d Child %s process run at %s" % (i, multiprocessing.current_process().name, time.time()))
time.sleep(1)
print("%d Child %s process end" % (i, multiprocessing.current_process().name,)) def main():
print("Process %d run" % (os.getpid()))
p2 = multiprocessing.Pool(multiprocessing.cpu_count())
for i in range(5):
p2.apply_async(run_process, args=(i,)) # 该函数用于启动进程,传递不定参数,主进程是非阻塞且支持结果返回进行回调。
p2.close() # 关闭进程池(pool),使其不在接受新的任务。
p2.join() # 主进程阻塞等待子进程的退出,join方法必须在close或terminate之后使用。
print("Process %d stop" % (os.getpid())) if __name__ == '__main__':
main()

运行结果:

 Process 29676 run
0 Child SpawnPoolWorker-1 process run at 1487744098.910444
1 Child SpawnPoolWorker-3 process run at 1487744098.931447
2 Child SpawnPoolWorker-4 process run at 1487744098.936447
3 Child SpawnPoolWorker-2 process run at 1487744098.96145
0 Child SpawnPoolWorker-1 process end
4 Child SpawnPoolWorker-1 process run at 1487744099.911545
1 Child SpawnPoolWorker-3 process end
2 Child SpawnPoolWorker-4 process end
3 Child SpawnPoolWorker-2 process end
4 Child SpawnPoolWorker-1 process end
Process 29676 stop

代码解读:

Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。

请注意输出的结果,task 0123是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成:

p2 = multiprocessing.Pool(5)

就可以同时跑5个进程。

由于Pool的默认大小是CPU的核数,如果你的电脑拥有8核CPU,你要提交至少9个子进程才能看到上面的等待效果。

pool类有一个map方法:  def map(self, func, iterable, chunksize=None) 与内置的map函数用法行为基本一致,它会使进程阻塞直到返回结果:

 def square(n):  # 计算平方值
time.sleep(1) #计算一次休眠1s
print(n*n,time.time())
return n*n def main():
print("Process %d run" % (os.getpid()))
number_list = [1, 2, 3, 4, 5, 6]
p2 = multiprocessing.Pool(multiprocessing.cpu_count()) # 本机4核CPU
p2.map(square, number_list)
print("Process %d stop" % (os.getpid())) if __name__ == '__main__':
main()

运行结果:

 Process 12264 run
1 1487744750.823629
4 1487744750.82863
9 1487744750.860633
16 1487744750.873634
25 1487744751.823729
36 1487744751.82873
Process 12264 stop

因为列表中共有6个元素,由于本机CPU有四核,在4个进程内的map方法同时可以对4个元素求平方,所以对于6个元素的列表,程序耗时2s。

由于map方法会使主进程阻塞,直到子进程返回,我们并没有调用p2.join(),主进程还是等待子进程结束

Python高级编程-多进程的更多相关文章

  1. 第十章:Python高级编程-多线程、多进程和线程池编程

    第十章:Python高级编程-多线程.多进程和线程池编程 Python3高级核心技术97讲 笔记 目录 第十章:Python高级编程-多线程.多进程和线程池编程 10.1 Python中的GIL 10 ...

  2. python并发编程&多进程(二)

    前导理论知识见:python并发编程&多进程(一) 一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_cou ...

  3. python高级之多进程

    python高级之多进程 本节内容 多进程概念 Process类 进程间通讯 进程同步 进程池 1.多进程概念 multiprocessing is a package that supports s ...

  4. 第八篇:python高级之多进程

    python高级之多进程   python高级之多进程 本节内容 多进程概念 Process类 进程间通讯 进程同步 进程池 1.多进程概念 multiprocessing is a package ...

  5. python高级编程:有用的设计模式3

    # -*- coding: utf-8 -*-__author__ = 'Administrator'#python高级编程:有用的设计模式#访问者:有助于将算法从数据结构中分离出来"&qu ...

  6. python高级编程:有用的设计模式2

    # -*- coding: utf-8 -*- __author__ = 'Administrator' #python高级编程:有用的设计模式 #代理 """ 代理对一 ...

  7. python高级编程:有用的设计模式1

    # -*- coding: utf-8 -*-__author__ = 'Administrator'#python高级编程:有用的设计模式#设计械是可复用的,某种程序上它对软件设计中觉问题提供的语言 ...

  8. python高级编程技巧

    由python高级编程处学习 http://blog.sina.com.cn/s/blog_a89e19440101fb28.html Python列表解析语法[]和生成 器()语法类似 [expr  ...

  9. python高级编程之选择好名称:完

    由于时间关系,python高级编程不在放在这边进行学习了,如果需要的朋友可以看下面的网盘进行下载 # # -*- coding: utf-8 -*- # # python:2.x # __author ...

随机推荐

  1. JavaScript document对象

    1.document对象是window对象的子对象,可直接使用,多用于获取HTML页面元素 2.document对象属性 a) alinkColor活动链接颜色 b) linkColor文本链接颜色 ...

  2. 【Django笔记二】Django2.0配置模板和静态文件

    一.环境版本信息: 操作系统:windows10 Django版本:2.0.5 Python版本:3.6.4 二.创建模板 1.在my_project文件夹下新建文件夹templates用于存放模板文 ...

  3. webpack-dev-server 多入口自动刷新,支持对象

    万物的来源~webpack 本身 watch webpack watch 传送门 webpack 可以监听文件变化,当它们修改后会重新编译 watch boolean 启用 Watch 模式.这意味着 ...

  4. SRM32(8)——ADC和DAC

    1.ADC简介 STM32 拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC)STM32F103至少拥有2个ADC,STM32F103ZE包含3个ADC,这些 ADC 可 ...

  5. python学习笔记:第13天 内置函数(一)

    详细文件查看点击这里:详细地址

  6. Leecode刷题之旅-C语言/python-349两个数组的交集

    /* * @lc app=leetcode.cn id=349 lang=c * * [349] 两个数组的交集 * * https://leetcode-cn.com/problems/inters ...

  7. linux 通过 openconnect 来连接学校内网

    参考 http://xingda1989.iteye.com/blog/1969908 https://blog.csdn.net/edin_blackpoint/article/details/70 ...

  8. 基于FDC2214的手势识别

    基于FDC2214的手势识别 1.本次题目来源于2018年全国电子设计大赛D题,要求实现对石头.剪刀.布以及数字12345的识别:同时在上述基础上实现对手势的学习. 2.硬件实现: 硬件主要采用STM ...

  9. (数据科学学习手札40)tensorflow实现LSTM时间序列预测

    一.简介 上一篇中我们较为详细地铺垫了关于RNN及其变种LSTM的一些基本知识,也提到了LSTM在时间序列预测上优越的性能,本篇就将对如何利用tensorflow,在实际时间序列预测任务中搭建模型来完 ...

  10. JVM基础知识及拓展

    我们可以吧JVM的基本结构分为四块:类加载器.执行引擎.运行时数据区和本地接口.一般来说Java程序在JVM中的执行流程如下: ①.首先我们会利用javac命令将我们所编写的.java源代码文件变异成 ...