简单学习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 相关文章推荐
python访问纯真IP数据库的代码
May 19 Python
用python实现面向对像的ASP程序实例
Nov 10 Python
Python中设置变量作为默认值时容易遇到的错误
Apr 03 Python
理解python正则表达式
Jan 15 Python
Python的爬虫框架scrapy用21行代码写一个爬虫
Apr 24 Python
详解Golang 与python中的字符串反转
Jul 21 Python
matplotlib简介,安装和简单实例代码
Dec 26 Python
详解python单元测试框架unittest
Jul 02 Python
tensorflow 重置/清除计算图的实现
Jan 19 Python
快速解决jupyter notebook启动需要密码的问题
Apr 21 Python
django实现日志按日期分割
May 21 Python
Python数据可视化常用4大绘图库原理详解
Oct 23 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
PHP 编程的 5个良好习惯
2009/02/20 PHP
frename PHP 灵活文件命名函数 frename
2009/09/09 PHP
php制作中间带自己定义图片二维码的方法
2014/01/27 PHP
php可应用于面包屑导航的递归寻找家谱树实现方法
2015/02/02 PHP
PHP后期静态绑定实例浅析
2018/12/21 PHP
PHP7生产环境队列Beanstalkd用法详解
2020/05/19 PHP
通过pjax实现无刷新翻页(兼容新版jquery)
2014/01/31 Javascript
js操作table元素实现表格行列新增、删除技巧总结
2015/11/18 Javascript
jQuery的实例及必知重要的jQuery选择器详解
2016/05/20 Javascript
jQuery实现简单倒计时功能的方法
2016/07/04 Javascript
关于JS中二维数组的声明方法
2016/09/24 Javascript
微信小程序scroll-view实现横向滚动和上拉加载示例
2017/03/06 Javascript
vue时间格式化实例代码
2017/06/13 Javascript
jQuery菜单实例(全选,反选,取消)
2017/08/28 jQuery
详解如何使用 vue-cli 开发多页应用
2017/12/16 Javascript
基于Vue的ajax公共方法(详解)
2018/01/20 Javascript
在小程序中集成redux/immutable/thunk第三方库的方法
2018/08/12 Javascript
如何检查一个对象是否为空
2019/04/11 Javascript
element-ui中Table表格省市区合并单元格的方法实现
2019/08/07 Javascript
iSlider手机端图片滑动切换插件使用详解
2019/12/24 Javascript
Antd下拉选择,自动匹配功能的实现
2020/10/24 Javascript
[01:06:32]DOTA2上海特级锦标赛D组资格赛#1 EG VS VP第一局
2016/02/28 DOTA
linux下python抓屏实现方法
2015/05/22 Python
Python对数据库操作
2016/03/28 Python
python函数局部变量、全局变量、递归知识点总结
2019/11/15 Python
使用CSS3的背景渐变Text Gradient 创建文字颜色渐变
2014/08/19 HTML / CSS
CSS3中currentColor关键字的妙用
2016/02/27 HTML / CSS
HTML5中5个简单实用的API(第二篇,含全屏、可见性、拍照、预加载、电池状态)
2014/05/07 HTML / CSS
在职人员函授期间自我评价分享
2013/11/08 职场文书
《蒙娜丽莎之约》教学反思
2014/02/27 职场文书
揭牌仪式主持词
2014/03/19 职场文书
大学生就业意向书范文
2014/04/01 职场文书
党校毕业心得体会
2014/09/13 职场文书
护士长2014年度工作总结
2014/11/11 职场文书
个人总结与自我评价2015
2015/03/11 职场文书
2015年学校关工委工作总结
2015/04/03 职场文书