快速了解Python并发编程的工程实现(下)
关于我
编程界的一名小程序猿,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。 联系:hylinux1024@gmail.com
0x00 使用进程实现并发
上一篇文章介绍了线程的使用。然而Python
中由于Global Interpreter Lock
(全局解释锁GIL
)的存在,每个线程在在执行时需要获取到这个GIL
,在同一时刻中只有一个线程得到解释锁的执行,Python
中的线程并没有真正意义上的并发执行,多线程的执行效率也不一定比单线程的效率更高。 如果要充分利用现代多核CPU
的并发能力,就要使用multipleprocessing
模块了。
0x01 multipleprocessing
与使用线程的threading
模块类似,multipleprocessing
模块提供许多高级API
。最常见的是Pool
对象了,使用它的接口能很方便地写出并发执行的代码。
from multiprocessing import Pooldef f(x):return x * xif __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 osdef echo(text):# 父进程IDprint("Process Parent ID : ", os.getppid())# 进程IDprint("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 Pooldef f(x):return x * xif __name__ == '__main__':with Pool(5) as p:# map方法的作用是将f()方法并发地映射到列表中的每个元素a = p.map(f, [1, 2, 3])print(a)# 异步执行mapb = 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, Queuedef 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, Pipedef 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 timedef 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
的常见用法,同时介绍了进程间的数据方式:队列和管道。最后简单了解了进程间的同步原语。
通过与上篇的对比学习,本文的内容应该是更加容易掌握的。
0x03 引用
- python-parallel-programmning-cookbook.readthedocs.io
- docs.python.org/3/library/t…
- docs.python.org/3.7/library…
- docs.python.org/3/glossary.…
- docs.python.org/3/library/c…
转载于:https://juejin.im/post/5cefdc60f265da1bca51c0cf
快速了解Python并发编程的工程实现(下)相关推荐
- python并发编程调优_Python并发编程-并发解决方案概述
Python并发编程-并发解决方案概述 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.并发和并行区别 1>.并行(parallel) 同时做某些事,可以互不干扰的同一个时 ...
- 《转载》Python并发编程之线程池/进程池--concurrent.futures模块
本文转载自 Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mul ...
- python并发编程之协程
python并发编程之协程 1.协程: 单线程实现并发 在应用程序里控制多个任务的切换+保存状态 优点: 应用程序级别速度要远远高于操作系统的切换 缺点: 多个任务一旦有一个阻塞没有切,整个线程都阻塞 ...
- 揭秘Python并发编程——协程
原文链接:https://baijiahao.baidu.com/s?id=1649450510185145678&wfr=spider&for=pc Python并发编程一直是进阶当 ...
- python并发编程之semaphore(信号量)_浅谈Python并发编程之进程(守护进程、锁、信号量)...
前言:本博文是对Python并发编程之进程的知识延伸,主要讲解:守护进程.锁.信号量. 友情链接: 一.守护进程(daemon) 1.1 守护进程概念 首先我们都知道:正常情况下,主进程默认等待子进程 ...
- Python并发编程理论篇
Python并发编程理论篇 前言 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例的人,却不知道如何去学习更加高深的知识 ...
- python 并发编程 多线程 目录
线程理论 python 并发编程 多线程 开启线程的两种方式 python 并发编程 多线程与多进程的区别 python 并发编程 多线程 Thread对象的其他属性或方法 python 并发编程 多 ...
- python 并发编程 多进程 目录
python multiprocessing模块 介绍 python 开启进程两种方法 python 并发编程 查看进程的id pid与父进程id ppid python 并发编程 多进程 Proce ...
- Python并发编程系列之多进程(multiprocessing)
1 引言 本篇博文主要对Python中并发编程中的多进程相关内容展开详细介绍,Python进程主要在multiprocessing模块中,本博文以multiprocessing种Process类为中心 ...
最新文章
- CSS三角形如何工作?
- 关于fullcalendar里显示json格式的events(java)
- 更新项目经常使用的Linux命令
- MysqlDataTruncation Mysql 数据插入错误
- git idea 图形化_Git大全,你所需要的Git资料都在这里
- Simple Web Server
- 单片机如何用普通电池供电?
- Python数组类型——列表(list)
- java对类数组进行排序_Java比较器类对数组进行排序
- Kotlin入门(28)Application单例化
- Flask和WebSocket
- 百度区块链 xuperchain 如何剪枝 裁剪区块
- (数据结构整理)NJUPT1054
- c246芯片组服务器主板,支持Xeon E-2100系列:ASRock 华擎 发布 C246M WS 主板
- Presto SQL 时间日期函数
- 微信小程序采坑三:输入框设置自动获取焦点后无法自动获取焦点
- vue3+Typescript---Composition API(常用部分)学习笔记(二)
- java HashMap集合的使用
- 数值计算之 最小二乘法(1)最小二乘计算与线性方程
- [转载](asp.net大型项目实践)
热门文章
- mysql 判断是否已存在及插入表数据 的 简单存储过程
- Android Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE解决办法
- ABI(Application Binary Interface)
- Java 对象初始化过程
- re 模块 分组特别说明
- Python中生成一个指定长度的随机字符串实现示例
- 上传jar包到Apache Archiva本地仓库
- Project Tango 的一些应用
- C++ 的多态性与虚函数
- Android Prelink实现的源码分析