一文了解Python并发编程的工程实现方法


Posted in Python onMay 31, 2019

上一篇文章介绍了线程的使用。然而 Python 中由于 Global Interpreter Lock (全局解释锁 GIL )的存在,每个线程在在执行时需要获取到这个 GIL ,在同一时刻中只有一个线程得到解释锁的执行, Python 中的线程并没有真正意义上的并发执行,多线程的执行效率也不一定比单线程的效率更高。 如果要充分利用现代多核 CPU 的并发能力,就要使用 multipleprocessing 模块了。

0x01 multipleprocessing

与使用线程的 threading 模块类似, multipleprocessing 模块提供许多高级 API 。最常见的是 Pool 对象了,使用它的接口能很方便地写出并发执行的代码。

from multiprocessing import Pool
def f(x):
 return x * x
if __name__ == '__main__':
 with Pool(5) as p:
  # map方法的作用是将f()方法并发地映射到列表中的每个元素
  print(p.map(f, [1, 2, 3]))
# 执行结果
# [1, 4, 9]

关于 Pool 下文中还会提到,这里我们先来看 Process 。

Process

要创建一个进程可以使用 Process 类,使用 start() 方法启动进程。

from multiprocessing import Process
import os
def echo(text):
 # 父进程ID
 print("Process Parent ID : ", os.getppid())
 # 进程ID
 print("Process PID : ", os.getpid())
 print('echo : ', text)
if __name__ == '__main__':
 p = Process(target=echo, args=('hello process',))
 p.start()
 p.join()
# 执行结果
# Process Parent ID : 27382
# Process PID : 27383
# echo : hello process

进程池

正如开篇提到的 multiprocessing 模块提供了 Pool 类可以很方便地实现一些简单多进程场景。 它主要有以下接口

  • apply(func[, args[, kwds]])
  • 执行 func(args,kwds) 方法,在方法结束返回前会阻塞。
  • apply_async(func[, args[, kwds[, callback[, error_callback]]]])
  • 异步执行 func(args,kwds) ,会立即返回一个 result 对象,如果指定了 callback 参数,结果会通过回调方法返回,还可以指定执行出错的回调方法 error_callback()
  • map(func, iterable[, chunksize])
  • 类似内置函数 map() ,可以并发执行 func ,是同步方法
  • map_async(func, iterable[, chunksize[, callback[, error_callback]]])
  • 异步版本的 map
  • close()
  • 关闭进程池。当池中的所有工作进程都执行完毕时,进程会退出。
  • terminate()
  • 终止进程池
  • join()
  • 等待工作进程执行完,必需先调用 close() 或者 terminate()
from multiprocessing import Pool
def f(x):
 return x * x
if __name__ == '__main__':
 with Pool(5) as p:
  # map方法的作用是将f()方法并发地映射到列表中的每个元素
  a = p.map(f, [1, 2, 3])
  print(a)
  # 异步执行map
  b = p.map_async(f, [3, 5, 7, 11])
  # b 是一个result对象,代表方法的执行结果
  print(b)
  # 为了拿到结果,使用join方法等待池中工作进程退出
  p.close()
  # 调用join方法前,需先执行close或terminate方法
  p.join()
  # 获取执行结果
  print(b.get())
# 执行结果
# [1, 4, 9]
# <multiprocessing.pool.MapResult object at 0x10631b710>
# [9, 25, 49, 121]

map_async() 和 apply_async() 执行后会返回一个 class multiprocessing.pool.AsyncResult 对象,通过它的 get() 可以获取到执行结果, ready() 可以判断 AsyncResult 的结果是否准备好。

进程间数据的传输

multiprocessing 模块提供了两种方式用于进程间的数据共享:队列( Queue )和管道( Pipe )

Queue 是线程安全,也是进程安全的。使用 Queue 可以实现进程间的数据共享,例如下面的 demo 中子进程 put 一个对象,在主进程中就能 get 到这个对象。 任何可以序列化的对象都可以通过 Queue 来传输。

from multiprocessing import Process, Queue
def f(q):
 q.put([42, None, 'hello'])
if __name__ == '__main__':
 # 使用Queue进行数据通信
 q = Queue()
 p = Process(target=f, args=(q,))
 p.start()
 # 主进程取得子进程中的数据
 print(q.get()) # prints "[42, None, 'hello']"
 p.join()
# 执行结果
# [42, None, 'hello']

Pipe() 返回一对通过管道连接的 Connection 对象。这两个对象可以理解为管道的两端,它们通过 send() 和 recv() 发送和接收数据。

from multiprocessing import Process, Pipe
def write(conn):
 # 子进程中发送一个对象
 conn.send([42, None, 'hello'])
 conn.close()
def read(conn):
 # 在读的进程中通过recv接收对象
 data = conn.recv()
 print(data)
if __name__ == '__main__':
 # Pipe()方法返回一对连接对象
 w_conn, r_conn = Pipe()
 wp = Process(target=write, args=(w_conn,))
 rp = Process(target=read, args=(r_conn,))
 wp.start()
 rp.start()
# 执行结果
# [42, None, 'hello']

需要注意的是,两个进程不能同时对一个连接对象进行 send 或 recv 操作。

同步

我们知道线程间的同步是通过锁机制来实现的,进程也一样。

from multiprocessing import Process, Lock
import time
def print_with_lock(l, i):
 l.acquire()
 try:
  time.sleep(1)
  print('hello world', i)
 finally:
  l.release()
def print_without_lock(i):
 time.sleep(1)
 print('hello world', i)
if __name__ == '__main__':
 lock = Lock()
 # 先执行有锁的
 for num in range(5):
  Process(target=print_with_lock, args=(lock, num)).start()
 # 再执行无锁的
 # for num in range(5):
 #  Process(target=print_without_lock, args=(num,)).start()

有锁的代码将每秒依次打印

hello world 0
hello world 1
hello world 2
hello world 3
hello world 4

如果执行无锁的代码,则在我的电脑上执行结果是这样的

hello worldhello world  0
1
hello world 2
hello world 3
hello world 4

除了 Lock ,还包括 RLock 、 Condition 、 Semaphore 和 Event 等进程间的同步原语。其用法也与线程间的同步原语很类似。 API 使用可以参考文末中引用的文档链接。

在工程中实现进程间的数据共享应当优先使用 队列或管道。

0x02 总结

本文对 multiprocessing 模块中常见的 API 作了简单的介绍。讲述了 Process 和 Pool 的常见用法,同时介绍了进程间的数据方式:队列和管道。最后简单了解了进程间的同步原语。

Python 相关文章推荐
在Django中创建URLconf相关的通用视图的方法
Jul 20 Python
Python数据结构与算法之字典树实现方法示例
Dec 13 Python
Python网络编程之TCP套接字简单用法示例
Apr 09 Python
Python基于pandas实现json格式转换成dataframe的方法
Jun 22 Python
对pandas的层次索引与取值的新方法详解
Nov 06 Python
Django 内置权限扩展案例详解
Mar 04 Python
Python第三方库face_recognition在windows上的安装过程
May 03 Python
Python模块future用法原理详解
Jan 20 Python
TensorFlow实现指数衰减学习率的方法
Feb 05 Python
Python实现AI换脸功能
Apr 10 Python
python爬虫使用requests发送post请求示例详解
Aug 05 Python
python实现邮件循环自动发件功能
Sep 11 Python
Python微信操控itchat的方法
May 31 #Python
python基于SMTP协议发送邮件
May 31 #Python
python 使用turtule绘制递归图形(螺旋、二叉树、谢尔宾斯基三角形)
May 30 #Python
Python自定义函数计算给定日期是该年第几天的方法示例
May 30 #Python
基于Python打造账号共享浏览器功能
May 30 #Python
Python实现带下标索引的遍历操作示例
May 30 #Python
Python动态参数/命名空间/函数嵌套/global和nonlocal
May 29 #Python
You might like
PHP上传文件参考配置大文件上传
2015/12/16 PHP
php登录超时检测功能实例详解
2017/03/21 PHP
Save a File Using a File Save Dialog Box
2007/06/18 Javascript
模拟jQuery ajax服务器端与客户端通信的代码
2011/03/28 Javascript
浅析jQuery(function(){})与(function(){})(jQuery)之间的区别
2014/01/09 Javascript
JS实现判断滚动条滚到页面底部并执行事件的方法
2014/12/18 Javascript
js实现背景图片感应鼠标变化的方法
2015/02/28 Javascript
jQuery使用after()方法在元素后面添加多项内容的方法
2015/03/26 Javascript
jquery简单实现外部链接用新窗口打开的方法
2015/05/30 Javascript
vue 表单输入格式化中文输入法异常问题
2018/05/30 Javascript
JS/jQuery实现简单的开关灯效果【案例】
2019/02/19 jQuery
微信公众平台 客服接口发消息的实现代码(Java接口开发)
2019/04/17 Javascript
vue.js基于v-for实现批量渲染 Json数组对象列表数据示例
2019/08/03 Javascript
微信小程序实现录音功能
2019/11/22 Javascript
[13:18]《一刀刀一天》之DOTA全时刻21:详解TI新赛制 A队再露獠牙
2014/06/24 DOTA
Using Django with GAE Python 后台抓取多个网站的页面全文
2016/02/17 Python
使用简单工厂模式来进行Python的设计模式编程
2016/03/01 Python
用PyInstaller把Python代码打包成单个独立的exe可执行文件
2018/05/26 Python
pycharm的console输入实现换行的方法
2019/01/16 Python
python numpy 按行归一化的实例
2019/01/21 Python
解决python中import文件夹下面py文件报错问题
2020/06/01 Python
HTML5 File接口在web页面上使用文件下载
2017/02/27 HTML / CSS
德国香水、化妆品和护理产品网上商店:Parfumdreams
2018/09/26 全球购物
西班牙家用电器和电子产品购物网站:Mi Electro
2019/02/25 全球购物
兼职学生的自我评价
2013/11/24 职场文书
销售业务员岗位职责
2014/01/29 职场文书
评析教师个人的自我评价
2014/02/19 职场文书
会计求职自荐信
2014/06/20 职场文书
助人为乐好少年事迹材料
2014/08/18 职场文书
学生会竞选演讲稿怎么写
2014/08/26 职场文书
2015年电教工作总结
2015/05/26 职场文书
交通安全教育主题班会
2015/08/12 职场文书
解析:创业计划书和商业计划书二者之间到底有什么区别
2019/08/14 职场文书
解决jupyter notebook启动后没有token的坑
2021/04/24 Python
2021年国漫热度排行前十,完美世界上榜,第四是美国动画作品
2022/03/18 国漫
为自由献出你的心脏!「进击的巨人展 FINAL」2022年6月在台开展
2022/04/13 日漫