简单学习Python多进程Multiprocessing


Posted in Python onAugust 29, 2017

1.1 什么是 Multiprocessing

多线程在同一时间只能处理一个任务。

可把任务平均分配给每个核,而每个核具有自己的运算空间。

1.2 添加进程 Process

与线程类似,如下所示,但是该程序直接运行无结果,因为IDLE不支持多进程,在命令行终端运行才有结果显示

import multiprocessing as mp

def job(a,b):
 print('abc')
if __name__=='__main__':
 p1=mp.Process(target=job,args=(1,2))
 p1.start()
 p1.join()

1.3 存储进程输出 Queue

不知道为什么下面的这个程序可以在IDLE中正常运行。首先定义了一个job函数作系列数学运算,然后将结果放到res中,在main函数运行,取出queue中存储的结果再进行一次加法运算。

import multiprocessing as mp

def job(q):
 res=0
 for i in range(1000):
 res+=i+i**2+i**3
 q.put(res)

 
if __name__ == '__main__':
 q=mp.Queue()
 p1 = mp.Process(target=job,args=(q,))#注意当参数只有一个时,应加上逗号
 p2 = mp.Process(target=job,args=(q,)) 
 p1.start()
 p2.start()
 
 p1.join()
 p2.join()
 res1=q.get()
 res2=q.get()
 print(res1+res2)

结果如下所示:

 简单学习Python多进程Multiprocessing

1.4 效率比对 threading & multiprocessing

在job函数中定义了数学运算,比较正常情况、多线程和多进程分别的运行时间。

import multiprocessing as mp
import threading as td
import time

def job(q):
 res = 0
 for i in range(10000000):
 res += i+i**2+i**3
 q.put(res) # queue

def multicore():
 q = mp.Queue()
 p1 = mp.Process(target=job, args=(q,))
 p2 = mp.Process(target=job, args=(q,))
 p1.start()
 p2.start()
 p1.join()
 p2.join()
 res1 = q.get()
 res2 = q.get()
 print('multicore:' , res1+res2)

def normal():
 res = 0
 for _ in range(2):#线程或进程都构造了两个,进行了两次运算,所以这里循环两次
 for i in range(10000000):
  res += i+i**2+i**3
 print('normal:', res)

def multithread():
 q = mp.Queue()
 t1 = td.Thread(target=job, args=(q,))
 t2 = td.Thread(target=job, args=(q,))
 t1.start()
 t2.start()
 t1.join()
 t2.join()
 res1 = q.get()
 res2 = q.get()
 print('multithread:', res1+res2)

if __name__ == '__main__':
 st = time.time()
 normal()
 st1= time.time()
 print('normal time:', st1 - st)
 multithread()
 st2 = time.time()
 print('multithread time:', st2 - st1)
 multicore()
 print('multicore time:', time.time()-st2)

在视频中的运行结果是多进程<正常<多线程,而我的运行结果为下图所示:

简单学习Python多进程Multiprocessing

综上,多核/多进程运行最快,说明在同时间运行了多个任务,而多线程却不一定会比正常情况下的运行来的快,这和多线程中的GIL有关。

1.5 进程池

进程池Pool,就是我们将所要运行的东西,放到池子里,Python会自行解决多进程的问题。

import multiprocessing as mp

def job(x):
 return x*x

def multicore():
 pool=mp.Pool(processes=2)#定义一个Pool,并定义CPU核数量为2
 res=pool.map(job,range(10))
 print(res)
 res=pool.apply_async(job,(2,))
 print(res.get())
 multi_res=[pool.apply_async(job,(i,)) for i in range(10)]
 print([res.get()for res in multi_res])

if __name__=='__main__':
 multicore()

运行结果如下所示:

简单学习Python多进程Multiprocessing

首先定义一个池子,有了池子之后,就可以让池子对应某一个函数,在上述代码中定义的pool对应job函数。我们向池子里丢数据,池子就会返回函数返回的值。 Pool和之前的Process的不同点是丢向Pool的函数有返回值,而Process的没有返回值。

接下来用map()获取结果,在map()中需要放入函数和需要迭代运算的值,然后它会自动分配给CPU核,返回结果

 简单学习Python多进程Multiprocessing

我们怎么知道Pool是否真的调用了多个核呢?我们可以把迭代次数增大些,然后打开CPU负载看下CPU运行情况

打开CPU负载(Mac):活动监视器 > CPU > CPU负载(单击一下即可)

Pool默认大小是CPU的核数,我们也可以通过在Pool中传入processes参数即可自定义需要的核数量。

Pool除了可以用map来返回结果之外,还可以用apply_async(),与map不同的是,只能传递一个值,只会放入一个核进行计算,但是传入值时要注意是可迭代的,所以在传入值后需要加逗号, 同时需要用get()方法获取返回值。所对应的代码为:

res=pool.apply_async(job,(2,))
print(res.get())

运行结果为4。

由于传入值是可以迭代的,则我们同样可以使用apply_async()来输出多个结果。如果在apply_async()中输入多个传入值:

res = pool.apply_async(job, (2,3,4,))

结果会报错:

TypeError: job() takes exactly 1 argument (3 given)

即apply_async()只能输入一组参数。

在此我们将apply_async()放入迭代器中,定义一个新的multi_res

multi_res = [pool.apply_async(job, (i,)) for i in range(10)]

同样在取出值时需要一个一个取出来

print([res.get() for res in multi_res])

apply用迭代器的运行结果与map取出的结果相同。

note:

(1)Pool默认调用是CPU的核数,传入processes参数可自定义CPU核数

(2)map() 放入迭代参数,返回多个结果

(3)apply_async()只能放入一组参数,并返回一个结果,如果想得到map()的效果需要通过迭代

1.6 共享内存 shared memory

只有通过共享内存才能让CPU之间进行交流。

通过Value将数据存储在一个共享的内存表中。

import multiprocessing as mp

value1 = mp.Value('i', 0) 
value2 = mp.Value('d', 3.14)

 其中,i和d表示数据类型。i为带符号的整型,d为双精浮点类型。更多数据类型可参考网址:https://docs.python.org/3/library/array.html

在多进程中有一个Array类,可以和共享内存交互,来实现进程之间共享数据。

和numpy中的不同,这里的Array只能是一维的,并且需要定义数据类型否则会报错。

array = mp.Array('i', [1, 2, 3, 4])

1.7 进程锁 Lock

首先是不加进程锁的运行情况,在下述代码中定义了共享变量v,定义了两个进程,均可对v进行操作。job函数的作用是每隔0.1s输出一次累加num的值,累加值num在两个进程中分别为1和3。

import multiprocessing as mp
import time

def job(v,num):
 for _ in range(10):
 time.sleep(0.1)#暂停0.1s,让输出效果更明显
 v.value+=num #v.value获取共享变量值
 print(v.value)
 
def multicore():
 v=mp.Value('i',0)#定义共享变量
 p1=mp.Process(target=job,args=(v,1))
 p2=mp.Process(target=job,args=(v,3))
 p1.start()
 p2.start()
 p1.join()
 p2.join()


if __name__=='__main__':
 multicore()

 运行结果如下所示:

简单学习Python多进程Multiprocessing

可以看到两个进程互相抢占共享内存v。

为了解决上述不同进程抢共享资源的问题,我们可以用加进程锁来解决。

首先需要定义一个进程锁:

l = mp.Lock() # 定义一个进程锁

然后将进程锁的信息传入各个进程中

p1 = mp.Process(target=job, args=(v,1,l)) # 需要将Lock传入
 p2 = mp.Process(target=job, args=(v,3,l))

在job()中设置进程锁的使用,保证运行时一个进程的对锁内内容的独占

def job(v, num, l):
 l.acquire() # 锁住
 for _ in range(5):
 time.sleep(0.1) 
 v.value += num # v.value获取共享内存
 print(v.value)
 l.release() # 释放

完整代码:

def job(v, num, l):
 l.acquire() # 锁住
 for _ in range(5):
 time.sleep(0.1) 
 v.value += num # 获取共享内存
 print(v.value)
 l.release() # 释放

def multicore():
 l = mp.Lock() # 定义一个进程锁
 v = mp.Value('i', 0) # 定义共享内存
 p1 = mp.Process(target=job, args=(v,1,l)) # 需要将lock传入
 p2 = mp.Process(target=job, args=(v,3,l)) 
 p1.start()
 p2.start()
 p1.join()
 p2.join()

if __name__ == '__main__':
 multicore()

运行结果如下所示:

简单学习Python多进程Multiprocessing

可以看到进程1运行完之后才运行进程2。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
解决python2.7 查询mysql时出现中文乱码
Oct 09 Python
Python编程求解二叉树中和为某一值的路径代码示例
Jan 04 Python
python多进程实现文件下载传输功能
Jul 28 Python
python版opencv摄像头人脸实时检测方法
Aug 03 Python
PyQT5 QTableView显示绑定数据的实例详解
Jun 25 Python
python批量解压zip文件的方法
Aug 20 Python
关于django 1.10 CSRF验证失败的解决方法
Aug 31 Python
python 哈希表实现简单python字典代码实例
Sep 27 Python
python中seaborn包常用图形使用详解
Nov 25 Python
Flask中sqlalchemy模块的实例用法
Aug 02 Python
套娃式文件夹如何通过Python批量处理
Aug 23 Python
装上这 14 个插件后,PyCharm 真的是无敌的存在
Jan 11 Python
Python简单实现自动删除目录下空文件夹的方法
Aug 29 #Python
Python实现文件内容批量追加的方法示例
Aug 29 #Python
Python实现解析Bit Torrent种子文件内容的方法
Aug 29 #Python
Python 3.x读写csv文件中数字的方法示例
Aug 29 #Python
在python3环境下的Django中使用MySQL数据库的实例
Aug 29 #Python
Python网络爬虫与信息提取(实例讲解)
Aug 29 #Python
Python开发的HTTP库requests详解
Aug 29 #Python
You might like
Laravel中错误与异常处理的用法示例
2018/09/16 PHP
PHPStorm 2020.1 调试 Nodejs的多种方法详解
2020/09/17 NodeJs
javascript 建设银行登陆键盘
2008/06/10 Javascript
最佳JS代码编写的14条技巧
2011/01/09 Javascript
javascript学习笔记(三)显示当时时间的代码
2011/04/08 Javascript
ScrollDown的基本操作示例
2013/06/09 Javascript
单击某一段文字改写文本颜色
2014/06/06 Javascript
js中直接声明一个对象的方法
2014/08/10 Javascript
js实现鼠标划过给div加透明度的方法
2015/05/25 Javascript
JavaScript清空数组元素的两种方法简单比较
2015/07/10 Javascript
JavaScript地理位置信息API
2016/06/11 Javascript
基于JS实现回到页面顶部的五种写法(从实现到增强)
2016/09/03 Javascript
footer定位页面底部(代码分享)
2017/03/07 Javascript
JS中正则表达式全局匹配模式 /g用法详解
2017/04/01 Javascript
基于VUE移动音乐WEBAPP跨域请求失败的解决方法
2018/01/16 Javascript
ES6学习笔记之map、set与数组、对象的对比
2018/03/01 Javascript
浅谈js中的bind
2019/03/18 Javascript
Angular7中创建组件/自定义指令/管道的方法实例详解
2019/04/02 Javascript
微信小程序获取用户信息及手机号(后端TP5.0)
2019/09/12 Javascript
Python使用bs4获取58同城城市分类的方法
2015/07/08 Python
python3 对list中每个元素进行处理的方法
2018/06/29 Python
10分钟教你用Python实现微信自动回复功能
2018/11/28 Python
Python高级编程之消息队列(Queue)与进程池(Pool)实例详解
2019/11/01 Python
检测浏览器对HTML5和CSS3支持度的方法
2015/06/25 HTML / CSS
英国领先的隐形眼镜在线供应商:Lenstore.co.uk
2019/11/24 全球购物
介绍一下linux的文件权限
2012/02/15 面试题
Linux内核产生并发的原因
2016/11/08 面试题
类的返射机制中的包及核心类
2016/09/12 面试题
高中自我鉴定
2013/12/20 职场文书
企业后勤岗位职责
2014/02/28 职场文书
青春寄语大全
2014/04/09 职场文书
我爱我的祖国演讲稿
2014/05/04 职场文书
中华魂放飞梦想演讲稿
2014/08/26 职场文书
停车场管理协议书范本
2014/10/08 职场文书
Python爬虫爬取全球疫情数据并存储到mysql数据库的步骤
2021/03/29 Python
SpringBoot+Vue+JWT的前后端分离登录认证详细步骤
2021/09/25 Java/Android