简单学习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操作MySQL数据库具体方法
Oct 28 Python
python函数参数*args**kwargs用法实例
Dec 04 Python
Java多线程编程中ThreadLocal类的用法及深入
Jun 21 Python
flask中使用蓝图将路由分开写在不同文件实例解析
Jan 19 Python
Python发送http请求解析返回json的实例
Mar 26 Python
Flask框架Jinjia模板常用语法总结
Jul 19 Python
python3.5基于TCP实现文件传输
Mar 20 Python
pyqt5 lineEdit设置密码隐藏,删除lineEdit已输入的内容等属性方法
Jun 24 Python
Python如何使用k-means方法将列表中相似的句子归类
Aug 08 Python
Python关键字及可变参数*args,**kw原理解析
Apr 04 Python
序列化Python对象的方法
Aug 01 Python
Django通过设置CORS解决跨域问题
Nov 26 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 的几个配置文件函数
2006/12/21 PHP
php采集速度探究总结(原创)
2008/04/18 PHP
php继承中方法重载(覆盖)的应用场合
2015/02/09 PHP
php随机生成数字字母组合的方法
2015/03/18 PHP
PHP面向对象程序设计高级特性详解(接口,继承,抽象类,析构,克隆等)
2016/12/02 PHP
Javascript基础_简单比较undefined和null 值
2016/06/14 Javascript
javascript 广告移动特效的实现代码
2016/06/25 Javascript
js实现canvas图片与img图片的相互转换的示例
2017/08/31 Javascript
php main 与 iframe 相互通讯类(js+php同域/跨域)
2017/09/14 Javascript
JS使用正则表达式找出最长连续子串长度
2017/10/26 Javascript
javascript中的数据类型检测方法详解
2019/08/07 Javascript
在vue中获取wangeditor的html和text的操作
2020/10/23 Javascript
浅谈Python生成器generator之next和send的运行流程(详解)
2017/05/08 Python
Python获取系统所有进程PID及进程名称的方法示例
2018/05/24 Python
python生成密码字典的方法
2018/07/06 Python
Python解决走迷宫问题算法示例
2018/07/27 Python
使用python Telnet远程登录执行程序的方法
2019/01/26 Python
python重试装饰器的简单实现方法
2019/01/31 Python
python 求1-100之间的奇数或者偶数之和的实例
2019/06/11 Python
使用Python给头像戴上圣诞帽的图像操作过程解析
2019/09/20 Python
基于pytorch 预训练的词向量用法详解
2020/01/06 Python
德国旅游网站:weg.de
2018/06/03 全球购物
Rhone官方网站:男士运动服装、健身服装和高级运动服
2019/05/01 全球购物
什么是GWT的Entry Point
2013/08/16 面试题
教育学专业毕业生的自我评价
2013/11/21 职场文书
宝宝周岁宴答谢词
2014/01/26 职场文书
致标枪运动员广播稿
2014/02/06 职场文书
幼儿园教师工作感言
2014/02/15 职场文书
俄语专业职业生涯规划
2014/02/26 职场文书
2014年冬季防火方案
2014/05/21 职场文书
党员志愿者活动方案
2014/08/28 职场文书
纪念九一八事变演讲稿:青少年应树立远大理想
2014/09/14 职场文书
幼儿园元旦主持词
2015/07/06 职场文书
家长会后的感想
2015/08/11 职场文书
解决Maven项目中 Invalid bound statement 无效的绑定问题
2021/06/15 Java/Android
django项目、vue项目部署云服务器的详细过程
2022/07/23 Servers