Python多进程并发与多线程并发编程实例总结
作者:cskchenshengkun 发布时间:2023-06-28 02:16:15
本文实例总结了Python多进程并发与多线程并发。分享给大家供大家参考,具体如下:
这里对python支持的几种并发方式进行简单的总结。
Python支持的并发分为多线程并发与多进程并发(异步IO本文不涉及)。概念上来说,多进程并发即运行多个独立的程序,优势在于并发处理的任务都由操作系统管理,不足之处在于程序与各进程之间的通信和数据共享不方便;多线程并发则由程序员管理并发处理的任务,这种并发方式可以方便地在线程间共享数据(前提是不能互斥)。Python对多线程和多进程的支持都比一般编程语言更高级,最小化了需要我们完成的工作。
一.多进程并发
Mark Summerfield指出,对于计算密集型程序,多进程并发优于多线程并发。计算密集型程序指的程序的运行时间大部分消耗在CPU的运算处理过程,而硬盘和内存的读写消耗的时间很短;相对地,IO密集型程序指的则是程序的运行时间大部分消耗在硬盘和内存的读写上,CPU的运算时间很短。
对于多进程并发,python支持两种实现方式,一种是采用进程安全的数据结构:multiprocessing.JoinableQueue,这种数据结构自己管理“加锁”的过程,程序员无需担心“死锁”的问题;python还提供了一种更为优雅而高级的实现方式:采用进程池。下面一一介绍。
1.队列实现——使用multiprocessing.JoinableQueue
multiprocessing是python标准库中支持多进程并发的模块,我们这里采用multiprocessing中的数据结构:JoinableQueue,它本质上仍是一个FIFO的队列,它与一般队列(如queue中的Queue)的区别在于它是多进程安全的,这意味着我们不用担心它的互斥和死锁问题。JoinableQueue主要可以用来存放执行的任务和收集任务的执行结果。举例来看(以下皆省去导入包的过程):
def read(q):
while True:
try:
value = q.get()
print('Get %s from queue.' % value)
time.sleep(random.random())
finally:
q.task_done()
def main():
q = multiprocessing.JoinableQueue()
pw1 = multiprocessing.Process(target=read, args=(q,))
pw2 = multiprocessing.Process(target=read, args=(q,))
pw1.daemon = True
pw2.daemon = True
pw1.start()
pw2.start()
for c in [chr(ord('A')+i) for i in range(26)]:
q.put(c)
try:
q.join()
except KeyboardInterrupt:
print("stopped by hand")
if __name__ == '__main__':
main()
对于windows系统的多进程并发,程序文件里必须含有“入口函数”(如main函数),且结尾处必须调用入口点。例如以if __name__ == '__main__': main()
结尾。
在这个最简单的多进程并发例子里,我们用多进程实现将26个字母打印出来。首先定义一个存放任务的JoinableQueue对象,然后实例化两个Process对象(每个对象对应一个子进程),实例化Process对象需要传送target和args参数,target是实现每个任务工作中的具体函数,args是target函数的参数。
pw1.daemon = True
pw2.daemon = True
这两句话将子进程设置为守护进程——主进程结束后随之结束。
pw1.start()
pw2.start()
一旦运行到这两句话,子进程就开始独立于父进程运行了,它会在单独的进程里调用target引用的函数——在这里即read函数,它是一个死循环,将参数q中的数一一读取并打印出来。
value = q.get()
这是多进程并发的要点,q是一个JoinableQueue对象,支持get方法读取第一个元素,如果q中没有元素,进程就会阻塞,直至q中被存入新元素。
因此执行完pw1.start()
pw2.start()
这两句话后,子进程虽然开始运行了,但很快就堵塞住。
for c in [chr(ord('A')+i) for i in range(26)]:
q.put(c)
将26个字母依次放入JoinableQueue对象中,这时候两个子进程不再阻塞,开始真正地执行任务。两个子进程都用value = q.get()来读取数据,它们都在修改q对象,而我们并不用担心同步问题,这就是multiProcessing.Joinable数据结构的优势所在——它是多进程安全的,它会自动处理“加锁”的过程。
try:
q.join()
q.join()
方法会查询q中的数据是否已读完——这里指的就是任务是否执行完,如果没有,程序会阻塞住等待q中数据读完才开始继续执行(可以用Ctrl+C强制停止)。
对Windows系统,调用任务管理器应该可以看到有多个子进程在运行。
2.进程池实现——使用concurrent.futures.ProcessPoolExecutor
Python还支持一种更为优雅的多进程并发方式,直接看例子:
def read(q):
print('Get %s from queue.' % q)
time.sleep(random.random())
def main():
futures = set()
with concurrent.futures.ProcessPoolExecutor() as executor:
for q in (chr(ord('A')+i) for i in range(26)):
future = executor.submit(read, q)
futures.add(future)
try:
for future in concurrent.futures.as_completed(futures):
err = future.exception()
if err is not None:
raise err
except KeyboardInterrupt:
print("stopped by hand")
if __name__ == '__main__':
main()
这里我们采用concurrent.futures.ProcessPoolExecutor对象,可以把它想象成一个进程池,子进程往里“填”。我们通过submit方法实例一个Future对象,然后把这里Future对象都填到池——futures里,这里futures是一个set对象。只要进程池里有future,就会开始执行任务。这里的read函数更为简单——只是把一个字符打印并休眠一会而已。
try:
for future in concurrent.futures.as_completed(futures):
这是等待所有子进程都执行完毕。子进程执行过程中可能抛出异常,err = future.exception()
可以收集这些异常,便于后期处理。
可以看出用Future对象处理多进程并发更为简洁,无论是target函数的编写、子进程的启动等等,future对象还可以向使用者汇报其状态,也可以汇报执行结果或执行时的异常。
二.多线程并发
对于IO密集型程序,多线程并发可能要优于多进程并发。因为对于网络通信等IO密集型任务来说,决定程序效率的主要是网络延迟,这时候是使用进程还是线程就没有太大关系了。
1.队列实现——使用queue.Queue
程序与多进程基本一致,只是这里我们不必使用multiProcessing.JoinableQueue
对象了,一般的队列(来自queue.Queue)就可以满足要求:
def read(q):
while True:
try:
value = q.get()
print('Get %s from queue.' % value)
time.sleep(random.random())
finally:
q.task_done()
def main():
q = queue.Queue()
pw1 = threading.Thread(target=read, args=(q,))
pw2 = threading.Thread(target=read, args=(q,))
pw1.daemon = True
pw2.daemon = True
pw1.start()
pw2.start()
for c in [chr(ord('A')+i) for i in range(26)]:
q.put(c)
try:
q.join()
except KeyboardInterrupt:
print("stopped by hand")
if __name__ == '__main__':
main()
并且这里我们实例化的是Thread对象,而不是Process对象,程序的其余部分看起来与多进程并没有什么两样。
2. 线程池实现——使用concurrent.futures.ThreadPoolExecutor
直接看例子:
def read(q):
print('Get %s from queue.' % q)
time.sleep(random.random())
def main():
futures = set()
with concurrent.futures.ThreadPoolExecutor(multiprocessing.cpu_count()*4) as executor:
for q in (chr(ord('A')+i) for i in range(26)):
future = executor.submit(read, q)
futures.add(future)
try:
for future in concurrent.futures.as_completed(futures):
err = future.exception()
if err is not None:
raise err
except KeyboardInterrupt:
print("stopped by hand")
if __name__ == '__main__':
main()
用ThreadPoolExecutor与用ProcessPoolExecutor看起来没什么区别,只是改了一下签名而已。
不难看出,不管是使用队列还是使用进/线程池,从多进程转化到多线程是十分容易的——仅仅是修改了几个签名而已。当然内部机制完全不同,只是python的封装非常好,使我们可以不用关心这些细节,这正是python优雅之处。
希望本文所述对大家Python程序设计有所帮助。
来源:http://blog.csdn.net/cskchenshengkun/article/details/45770627


猜你喜欢
- django接口可以通过localhost或者127.0.0.1进行访问,但无法通过本机ip地址访问1. 修改django项目中的setti
- 内容摘要: 首先来讲讲 Session 的好处,它可以用来记录客户端私有的
- Django结合ajax进行页面实时更新踩过的坑简单记录一下在使用Django、echarts和ajax实现数据动态更新时遇到的一些坑: 1
- (一)Python 是如何进行内存管理的?答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制⒈对象的引用计数机制Pyth
- PIL vs PillowPIL: Python Imaging Library,是python的图像处理库。由于PIL不兼容setupto
- 环境 : Debian 6.02 (squeeze) x64 软件:MySQL 5.5.14 步骤: 1、升级系统 #apt-get upd
- 一 引入jwtjwt用户身份验证go get github.com/dgrijalva/jwt-go二 框架中引入jwtimport (&q
- 安装:cnpm i -S jwt-decode引入:import jwt_decode from "jwt-decode"
- 译注:这是一篇在Stack overflow上很热的帖子。提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(me
- 目录一、使用方法二、输出结果1.id2.select_type3.table4.partitions5.type6.possible_key
- 属性在运行时的动态替换,叫做猴子补丁(Monkey Patch)。为什么叫猴子补丁属性的运行时替换和猴子也没什么关系,关于猴子补丁的由来网上
- 本文实例为大家分享了vue中使用svg封装全局消息提示组件的具体代码,供大家参考,具体内容如下先看效果图一、首先安装下载需要用到的svg相关
- 一、图图:数据(张量Tenrsor)+ 操作(节点Operation) (静态)图可以用:1、默认图;2、自定义图。1、默认图查看默认图的方
- 前言这篇文章主要记录一下平时自己实践得到的, 博客中学习的以及在一些项目源码中看到的 javascript 技巧。有些东西可以说是奇淫技巧,
- I/O吞吐量小,形成了瓶颈效应。 没有创建计算列导致查询不优化。 内存不足。 网络速度慢。 查询出的数据量过大(可以采用多次查询,其他的方法
- 一个不错的js效果,实现了图片预加载,并实时显示图片加载进度。<script> var l=0; var i
- 插入排序插入排序是一种简单的排序算法,以数组为例,我们可以把数组看成是多个数组组成。插入排序的基本思想是往前面已排好序的数组中插入一个元素,
- 对于经常需要表格头部不东,而列表可以滚动,多用于数据比较多的情况,方便查看<!DOCTYPE HTML PUBLIC "-/
- 前言总之,两者都是用来重塑tensor的shape的。view只适合对满足连续性条件(contiguous)的tensor进行操作,而res
- 1,安装Redis,根据自己的PHP版本安装对应的redis扩展(此步骤简单的描述一下)1.1,安装 php_igbinary.dll,ph