一篇文章搞定Python多进程(全)
1.Python多进程模块
Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Process对象来创建一个进程对象。这个进程对象的方法和线程对象的方法差不多也有start(), run(), join()等方法,其中有一个方法不同Thread线程对象中的守护线程方法是setDeamon,而Process进程对象的守护进程是通过设置daemon属性来完成的。
下面说说Python多进程的实现方法,和多线程类似
2.Python多进程实现方法一
from multiprocessing import Process def fun1(name):
print('测试%s多进程' %name) if __name__ == '__main__':
process_list = []
for i in range(5): #开启5个子进程执行fun1函数
p = Process(target=fun1,args=('Python',)) #实例化进程对象
p.start()
process_list.append(p) for i in process_list:
p.join() print('结束测试')
结果
测试Python多进程
测试Python多进程
测试Python多进程
测试Python多进程
测试Python多进程
结束测试 Process finished with exit code 0
上面的代码开启了5个子进程去执行函数,我们可以观察结果,是同时打印的,这里实现了真正的并行操作,就是多个CPU同时执行任务。我们知道进程是python中最小的资源分配单元,也就是进程中间的数据,内存是不共享的,每启动一个进程,都要独立分配资源和拷贝访问的数据,所以进程的启动和销毁的代价是比较大了,所以在实际中使用多进程,要根据服务器的配置来设定。
3.Python多进程实现方法二
还记得python多线程的第二种实现方法吗?是通过类继承的方法来实现的,python多进程的第二种实现方式也是一样的
from multiprocessing import Process class MyProcess(Process): #继承Process类
def __init__(self,name):
super(MyProcess,self).__init__()
self.name = name def run(self):
print('测试%s多进程' % self.name) if __name__ == '__main__':
process_list = []
for i in range(5): #开启5个子进程执行fun1函数
p = MyProcess('Python') #实例化进程对象
p.start()
process_list.append(p) for i in process_list:
p.join() print('结束测试')
结果
测试Python多进程
测试Python多进程
测试Python多进程
测试Python多进程
测试Python多进程
结束测试 Process finished with exit code 0
效果和第一种方式一样。
我们可以看到Python多进程的实现方式和多线程的实现方式几乎一样。
Process类的其他方法
构造方法: Process([group [, target [, name [, args [, kwargs]]]]])
group: 线程组
target: 要执行的方法
name: 进程名
args/kwargs: 要传入方法的参数 实例方法:
is_alive():返回进程是否在运行,bool类型。
join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
start():进程准备就绪,等待CPU调度
run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
terminate():不管任务是否完成,立即停止工作进程 属性:
daemon:和线程的setDeamon功能一样
name:进程名字
pid:进程号
关于join,daemon的使用和python多线程一样,这里就不在复述了,大家可以看看以前的python多线程系列文章。
4.Python多线程的通信
进程是系统独立调度核分配系统资源(CPU、内存)的基本单位,进程之间是相互独立的,每启动一个新的进程相当于把数据进行了一次克隆,子进程里的数据修改无法影响到主进程中的数据,不同子进程之间的数据也不能共享,这是多进程在使用中与多线程最明显的区别。但是难道Python多进程中间难道就是孤立的吗?当然不是,python也提供了多种方法实现了多进程中间的通信和数据共享(可以修改一份数据)
进程对列Queue
Queue在多线程中也说到过,在生成者消费者模式中使用,是线程安全的,是生产者和消费者中间的数据管道,那在python多进程中,它其实就是进程之间的数据管道,实现进程通信。
from multiprocessing import Process,Queue def fun1(q,i):
print('子进程%s 开始put数据' %i)
q.put('我是%s 通过Queue通信' %i) if __name__ == '__main__':
q = Queue() process_list = []
for i in range(3):
p = Process(target=fun1,args=(q,i,)) #注意args里面要把q对象传给我们要执行的方法,这样子进程才能和主进程用Queue来通信
p.start()
process_list.append(p) for i in process_list:
p.join() print('主进程获取Queue数据')
print(q.get())
print(q.get())
print(q.get())
print('结束测试')
结果
子进程0 开始put数据
子进程1 开始put数据
子进程2 开始put数据
主进程获取Queue数据
我是0 通过Queue通信
我是1 通过Queue通信
我是2 通过Queue通信
结束测试 Process finished with exit code 0
上面的代码结果可以看到我们主进程中可以通过Queue获取子进程中put的数据,实现进程间的通信。
管道Pipe
管道Pipe和Queue的作用大致差不多,也是实现进程间的通信,下面之间看怎么使用吧
from multiprocessing import Process, Pipe
def fun1(conn):
print('子进程发送消息:')
conn.send('你好主进程')
print('子进程接受消息:')
print(conn.recv())
conn.close() if __name__ == '__main__':
conn1, conn2 = Pipe() #关键点,pipe实例化生成一个双向管
p = Process(target=fun1, args=(conn2,)) #conn2传给子进程
p.start()
print('主进程接受消息:')
print(conn1.recv())
print('主进程发送消息:')
conn1.send("你好子进程")
p.join()
print('结束测试')
结果
主进程接受消息:
子进程发送消息:
子进程接受消息:
你好主进程
主进程发送消息:
你好子进程
结束测试 Process finished with exit code 0
上面可以看到主进程和子进程可以相互发送消息
Managers
Queue和Pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。那么久要用到Managers
from multiprocessing import Process, Manager def fun1(dic,lis,index): dic[index] = 'a'
dic[''] = 'b'
lis.append(index) #[0,1,2,3,4,0,1,2,3,4,5,6,7,8,9]
#print(l) if __name__ == '__main__':
with Manager() as manager:
dic = manager.dict()#注意字典的声明方式,不能直接通过{}来定义
l = manager.list(range(5))#[0,1,2,3,4] process_list = []
for i in range(10):
p = Process(target=fun1, args=(dic,l,i))
p.start()
process_list.append(p) for res in process_list:
res.join()
print(dic)
print(l)
结果:
{0: 'a', '': 'b', 3: 'a', 1: 'a', 2: 'a', 4: 'a', 5: 'a', 7: 'a', 6: 'a', 8: 'a', 9: 'a'}
[0, 1, 2, 3, 4, 0, 3, 1, 2, 4, 5, 7, 6, 8, 9]
可以看到主进程定义了一个字典和一个列表,在子进程中,可以添加和修改字典的内容,在列表中插入新的数据,实现进程间的数据共享,即可以共同修改同一份数据
5.进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。就是固定有几个进程可以使用。
进程池中有两个方法:
apply:同步,一般不使用
apply_async:异步
from multiprocessing import Process,Pool
import os, time, random def fun1(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start))) if __name__=='__main__':
pool = Pool(5) #创建一个5个进程的进程池 for i in range(10):
pool.apply_async(func=fun1, args=(i,)) pool.close()
pool.join()
print('结束测试')
结果
Run task 0 (37476)...
Run task 1 (4044)...
Task 0 runs 0.03 seconds.
Run task 2 (37476)...
Run task 3 (17252)...
Run task 4 (16448)...
Run task 5 (24804)...
Task 2 runs 0.27 seconds.
Run task 6 (37476)...
Task 1 runs 0.58 seconds.
Run task 7 (4044)...
Task 3 runs 0.98 seconds.
Run task 8 (17252)...
Task 5 runs 1.13 seconds.
Run task 9 (24804)...
Task 6 runs 1.46 seconds.
Task 4 runs 2.73 seconds.
Task 8 runs 2.18 seconds.
Task 7 runs 2.93 seconds.
Task 9 runs 2.93 seconds.
结束测试
对Pool
对象调用join()
方法会等待所有子进程执行完毕,调用join()
之前必须先调用close()
,调用close()
之后就不能继续添加新的Process
了。
进程池map方法
案例来源于网络,侵权请告知,谢谢
因为网上看到这个例子觉得不错,所以这里就不自己写案例,这个案例比较有说服力
import os
import PIL from multiprocessing import Pool
from PIL import Image SIZE = (75,75)
SAVE_DIRECTORY = \'thumbs\' def get_image_paths(folder):
return (os.path.join(folder, f)
for f in os.listdir(folder)
if \'jpeg\' in f) def create_thumbnail(filename):
im = Image.open(filename)
im.thumbnail(SIZE, Image.ANTIALIAS)
base, fname = os.path.split(filename)
save_path = os.path.join(base, SAVE_DIRECTORY, fname)
im.save(save_path) if __name__ == \'__main__\':
folder = os.path.abspath(
\'11_18_2013_R000_IQM_Big_Sur_Mon__e10d1958e7b766c3e840\')
os.mkdir(os.path.join(folder, SAVE_DIRECTORY)) images = get_image_paths(folder) pool = Pool()
pool.map(creat_thumbnail, images) #关键点,images是一个可迭代对象
pool.close()
pool.join()
上边这段代码的主要工作就是将遍历传入的文件夹中的图片文件,一一生成缩略图,并将这些缩略图保存到特定文件夹中。这我的机器上,用这一程序处理 6000 张图片需要花费 27.9 秒。 map 函数并不支持手动线程管理,反而使得相关的 debug 工作也变得异常简单。
map在爬虫的领域里也可以使用,比如多个URL的内容爬取,可以把URL放入元祖里,然后传给执行函数。
一篇文章搞定Python多进程(全)的更多相关文章
- 一篇文章搞定百度OCR图片文字识别API
一篇文章搞定百度OCR图片文字识别API https://www.jianshu.com/p/7905d3b12104
- 一篇文章搞定Git——Git代码管理及使用规范
一篇文章搞定Git--Git代码管理及使用规范 https://blog.csdn.net/weixin_42092278/article/details/90448721
- Python正则表达式很难?一篇文章搞定他,不是我吹!
1. 正则表达式语法 1.1 字符与字符类 1 特殊字符:.^$?+*{}| 以上特殊字符要想使用字面值,必须使用进行转义 2 字符类 1. 包含在[]中的一个或者多个字符被称为字符类,字符类在匹配时 ...
- pymongo 一篇文章搞定
一 安装 pip install pymongo 二 python连接mongodb数据库的前提 确保pymongo安装完毕 mongodb数据库的服务器端(mongod)必须处于启动状态 三 连接m ...
- 一篇文章搞定mongodb
一 安装 1 安装目录下新建文件夹data,etc,logs #在bin文件下启动cmd,指定数据存储的路径mongod --dbpath D:\MongoDB\data\db 2 etc文件夹中新建 ...
- 一篇文章搞定JS类型转换
啥要说这个东西?一道面试题就给我去说它的动机.题如下: var bool = new Boolean(false); if (bool) { alert('true'); } else { alert ...
- 一篇文章搞定git
git学习 一.背景(当成故事去读) Linus 虽然创建了 Linux,但 Linux 的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为 Linux 编写代码,那 Linux 的代码是如何管 ...
- 一篇文章搞懂Python装饰器所有用法
01. 装饰器语法糖 如果你接触 Python 有一段时间了的话,想必你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖. 它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上 ...
- 一篇文章搞定 Nginx 反向代理与负载均衡
代理 要想弄明白反向代理,首先要知道啥是正向代理,要搞懂正向代理只需要知道啥是代理即可.代理其实就是一个中介,在不同事物或同一事物内部起到居间联系作用的环节.比如买票黄牛,房屋中介等等. 在互联网中代 ...
随机推荐
- Yii ActiveRecord用法记录备忘
ActiveRecord 使用方法 Example1 in查询 $criteria = new CDbCriteria(); $criteria->select = $select; $crit ...
- Linux x64 Hadoop-2.4.1配置-解决错误Unable to load native-hadoop library for your platform
网上配置hadoop的教程一堆,各不尽相同,但没有一个是完整系统的. 下面给出遇到的错误的解决方法,相信能解决很多人的问题. 错误:Exception in thread "main&quo ...
- SpringBoot整合MybatisPlus3.X之分页插件(四)
注:详细请看2.X博客中,3.X直接上代码. 建议装一个MybatisX插件,可以在Mapper和Xml来回切换 pom.xml <dependencies> <dependency ...
- CVE-2019-16097:Harbor任意管理员注册漏洞复现
0x00 Harbor简介 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全.标识和管理等,扩展了开源Docker Distri ...
- 中小学生试卷自动生成程序--jialin大佬代码分析
结对编程代码评价 有幸和小jialin结对编程.拿到jialin的代码后. 我先是尝试用idea运行.结果报了如下错误. 无法加载主类,再尝试用eclipse运行. 好的,可以运行,那为什么用idea ...
- C Primer Plus (一)
摘要:重读C Primer Plus ,查漏补缺 重读C Primer Plus,记录遗漏的.未掌握的.不清楚的知识点. 一.概览 1.链接器的作用是将这3个目标元素(目标代码.系统的标准启动代码和库 ...
- 石头剪刀步(rps):dp,概率&期望
既然已经给std了,直接扔代码啦.代码注释还是不错哒. 因为我也有点懵,不明白的或有不同见解的一定要在评论区喷我啊! #include<bits/stdc++.h> using names ...
- Oracle“ORA-00911: 无效字符”解决方式
在工作中碰到ORA-00911:无效字符的问题, 代码如下: <select id="querySendMsg" parameterType="map" ...
- 【转载】InstantRun 原理——深度剖析 AndroidStudio 2.0
一.前言 Android Studio 2.0开始支持 Instant Run 特性, 使得在开发过程中能快速将代码变化更新到设备上.之前,更新代码之后需要先编译一个完整的新Apk,卸载设备上已安装的 ...
- 虚拟机操作系统内设置固定IP以及克隆虚拟机
以下为我自己整理的克隆虚拟机和设置固定IP的方法,记录一下,以防忘记: 桥接模式网络配置 1.配置ip地址等信息在/etc/sysconfig/network-scripts/ifcfg-ens33文 ...