Python线程池与进程池

前言

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:1097524789

前面我们已经将线程并发编程与进程并行编程全部摸了个透,其实我第一次学习他们的时候感觉非常困难甚至是吃力。因为概念实在是太多了,各种锁,数据共享同步,各种方法等等让人十分头痛。所以这边要告诉你一个好消息,前面的所有学习的知识点其实都是为本章知识点做铺垫,在学习了本章节的内容后关于如何使用多线程并发与多进程并行就采取本章节中介绍的方式即可。

这里要介绍一点与之前内容不同的地方 ,即如果使用队列进行由进程池创建的进程之间数据共享的话不管是 multiprocessing 模块下的 Queue 还是 queue 模块下的 Queue 都不能为进程池中所创建的进程进行数据共享,我们需要用到另一个队列即 中的 。 当然这个我也会在下面介绍到。那么开始学习吧!

官方文档

执行器

最早期的Python2中是没有线程池这一概念的,只有进程池。直到Python3的出现才引入了线程池,其实关于他们的使用都是非常简单,而且接口也是高度统一甚至说一模一样的。 而线程池与进程池的作用即是为了让我们能够更加便捷的管理线程或进程。

我们先说一下,如果需要使用线程池或进程池,需要导入模块 concurrent.futures 。

from concurrent.futures import ThreadPoolExecutor # 线程池执行器

from concurrent.futures import ProcessPoolExecutor # 进程池执行器

这里介绍一下,关于线程池或者进程池创建出的线程与进程与我们使用 multiprocessing 模块或者 threading 模块中创建的线程或进程有什么区别。我们以多线程为例:

import threadingdef task():ident = threading.get_ident()print(ident)# 销毁当前执行任务的线程if __name__ == '__main__':for i in range(10):t1 = threading.Thread(target=task,)  # 领任务t1.start()  # 等待CPU调度,而不是立即执行  # 执行# ==== 执行结果 ==== Ps:可以看到每个线程的id号都不一样,这也印证了图上说的。"""
10392
12068
5708
13864
2604
7196
7324
9728
9664
472
"""
import threading
from concurrent.futures import ThreadPoolExecutor  # 线程池执行器def task():ident = threading.get_ident()print(ident)# 结束任务,不销毁当前执行任务的线程,直到所有任务都执行完毕。if __name__ == '__main__':pool = ThreadPoolExecutor(max_workers=2)  # 这里代表有2个线程可以领取任务for i in range(10):pool.submit(task)  # 执行器启动任务,将这些任务给2个人分配,也就是说task这个任务会被这2个线程不断的执行,直到执行完毕后这2个线程才会死亡# ==== 执行结果 ==== Ps:可以看到这里都让这2个线程把任务接了,内存开销相比于上面的要小。"""
7272
7272
7272
7272
11596
7272
11596
11596
11596
11596
"""

方法大全

执行器方法大全
submit( fn , * args, * *kwargs) 调度可调用对象 fn ,以 fn(*args **kwargs) 方式执行并返回 Future 对象代表可调用对象的执行。(异步提交!极为牛逼!)
map( func , *iterables , timeout=None , chunksize=1 ) 类似于 map(func, *iterables) 。
shutdown( wait=True ) 等待,类似 join() 方法,并且在所有的任务完成后关闭执行器。 wait=True 为关闭,为 False 则是不关闭执行器的意思。
Ps:其实对于线程池或进程池来说,他们的池都有一个官方的名称叫做执行器,接口都是一样的。那么接下来我就将线程池进程池这样的名字换做执行器了,也是方便理解。  

基本使用

其实关于执行器的使用,我们有两种方式,一种是依赖于 with 语句,一种是不依赖于 with 语句,那么我在这里推荐使用依赖于wait语句的执行器。

不依赖于 with 语句的执行器使用:

import threading
from concurrent.futures import ThreadPoolExecutor  # 线程池执行器def task():print("执行了")if __name__ == '__main__':pool = ThreadPoolExecutor(max_workers=2)  # 这里代表有2个线程可以领取任务 , 对于线程池来讲它是默认值是CPU核心数+4,对于进程池来讲最大开启的进程数是CPU核心数。for i in range(10):pool.submit(task)  # 执行器启动任务,将这些任务给2个人分配,也就是说task这个任务会被这2个线程不断的执行,直到执行完毕后这2个线程才会死亡# ==== 执行结果 ==== Ps:可以看到这里都让这2个线程把任务接了,内存开销相比于上面的要小。"""
执行了
执行了
执行了
执行了
执行了
执行了
执行了
执行了
执行了
执行了
"""

依赖于 with 语句的执行器使用:

import threading
from concurrent.futures import ThreadPoolExecutor  # 线程池执行器def task():print("执行了")# 销毁if __name__ == '__main__':with ThreadPoolExecutor(max_workers=2) as pool:  # 这里代表有2个线程可以领取任务 , 对于线程池来讲它是默认值是CPU核心数+4,对于进程池来讲最大开启的进程数是CPU核心数。for i in range(10):pool.submit(task)  # 执行器启动任务,将这些任务给2个人分配,也就是说task这个任务会被这2个线程不断的执行,直到执行完毕后这2个线程才会死亡# ==== 执行结果 ==== Ps:可以看到这里都让这2个线程把任务接了,内存开销相比于上面的要小。"""
执行了
执行了
执行了
执行了
执行了
执行了
执行了
执行了
执行了
执行了
"""

期程对象

方法大全

期程对象(由执行器执行的任务的返回结果)方法大全
方法/属性名称 功能描述
cancel() 尝试取消调用。 如果调用正在执行或已结束运行不能被取消则该方法将返回 False ,否则调用会被取消并且该方法将返回 True 。
cancelled() 如果调用成功取消返回 True 。
running() 如果调用正在执行而且不能被取消那么返回 True 。
done() 如果调用已被取消或正常结束那么返回 True 。
result( timeout=None ) 即获取任务的返回结果,最大等待timeout秒,如不设置则死等,超时触发 CancelledError 异常。
add_done_callback( fn) 增加回调函数 fn ,这个 fn 应该至少有一个形参来接收当前期程对象。
exception( timeout=None ) 返回由调用引发的异常。如果调用还没完成那么这个方法将等待 timeout 秒。如果在 timeout 秒内没有执行完成, concurrent.futures.TimeoutError 将会被触发。 timeout 可以是整数或浮点数。如果 timeout 没有指定或为 ,那么等待时间就没有限制。
Ps:还有一些期程对象的方法没有举例出来。详情参见文档  

期程对象的作用

我们可以看到,我们上面的函数并没有返回值,如果有返回值的话怎么办呢?

import threading
from concurrent.futures import ThreadPoolExecutor  # 线程池执行器def task():print("执行了")return "玫瑰花"# 销毁if __name__ == '__main__':with ThreadPoolExecutor(max_workers=2) as pool:res = pool.submit(task)print(res)  # <Future at 0x2539ea97850 state=finished returned str> 这个就是期程对象,可以看到他里面还有当前任务的执行状态。 finished = 执行完了的意思print(res.result())  # 通过该方法就可以拿到任务的返回结果# ==== 执行结果 ===="""
执行了
<Future at 0x2539ea97850 state=finished returned str>
玫瑰花
"""

期程对象,也被称为未来对象,是一个非常重要的概念。这里可以记一笔,在 Django 框架中也有些地方采取了期程对象这样的设定,这是后话,后面再聊。

期程对象如何获取返回结果

我们尝试着将它的任务数量增多,发现使用期程对象直接获取任务结果会导致阻塞,怎么解决?

import time
import threading
from concurrent.futures import ThreadPoolExecutor  # 线程池执行器def task(x):print("执行了,这是第%s个任务"%x)time.sleep(3)return "玫瑰花"# 销毁if __name__ == '__main__':with ThreadPoolExecutor(max_workers=2) as pool:for i in range(10):res = pool.submit(task,i)print(res.result())  # 每次获取结果的时候都是阻塞,怎么办?这个速率就变得非常的Low逼了。# ==== 执行结果 ===="""
执行了,这是第0个任务
玫瑰花
执行了,这是第1个任务
玫瑰花
执行了,这是第2个任务
玫瑰花
执行了,这是第3个任务
玫瑰花
执行了,这是第4个任务
玫瑰花
执行了,这是第5个任务
玫瑰花
执行了,这是第6个任务
玫瑰花
执行了,这是第7个任务
玫瑰花
执行了,这是第8个任务
玫瑰花
执行了,这是第9个任务
玫瑰花
"""

我这里有一个办法,可以值得尝试一下。就是执行器本身有个方法 shutdown(wait=True) ,它会导致当前主线程的阻塞。那么我们就可以这样操作,主程序阻塞住,再将启程对象全部放到一个列表中,当所有任务处理完毕后阻塞通行,这个时候我们再循环这个列表拿出其中的结果。

import time
import threading
from concurrent.futures import ThreadPoolExecutor  # 线程池执行器def task(x):print("执行了,这是第%s个任务"%x)time.sleep(3)return "玫瑰花"# 销毁if __name__ == '__main__':res_list = [] # 用于存放所有期程对象with ThreadPoolExecutor(max_workers=2) as pool:for i in range(10):res = pool.submit(task,i)res_list.append(res)  # 将期程对象放入列表pool.shutdown(wait=True)  # 代表必须将所有子线程的任务跑完再继续向下执行主线程。for i in res_list:print(i.result())# ==== 执行结果 ===="""
执行了,这是第0个任务
执行了,这是第1个任务
执行了,这是第2个任务
执行了,这是第3个任务
执行了,这是第4个任务
执行了,这是第5个任务
执行了,这是第6个任务
执行了,这是第7个任务
执行了,这是第8个任务
执行了,这是第9个任务
玫瑰花
玫瑰花
玫瑰花
玫瑰花
玫瑰花
玫瑰花
玫瑰花
玫瑰花
玫瑰花
玫瑰花
"""

如果你觉得这种方法很赞,我只能送你两个字,太low了。我们注意执行器的 submit() 方法,这玩意儿是异步提交。异步提交的结果需要用到回调函数来进行调用,我们来看一下它有多牛逼。

回调函数

import time
import threading
from concurrent.futures import ThreadPoolExecutor  # 线程池执行器def task(x):print("执行了,这是第%s个任务"%x)time.sleep(3)return "玫瑰花"# 销毁def callback(res): # 必须有一个形参,来接收期程对象print(res.result())   # 打印结果,即task任务的返回结果if __name__ == '__main__':with ThreadPoolExecutor(max_workers=2) as pool:for i in range(10):res = pool.submit(task,i)res.add_done_callback(callback)  # <--- 增加回调函数,当期程对象中的任务处理状态完毕后将自动调用回调函数# ==== 执行结果 ==== # 异步提交牛逼不?只要任务返回了我们立马就可以获取到结果进行处理。"""
执行了,这是第0个任务
执行了,这是第1个任务
玫瑰花
玫瑰花
执行了,这是第2个任务
执行了,这是第3个任务
玫瑰花
玫瑰花
执行了,这是第4个任务
执行了,这是第5个任务
玫瑰花
玫瑰花
执行了,这是第6个任务
执行了,这是第7个任务
玫瑰花
玫瑰花
执行了,这是第8个任务
执行了,这是第9个任务
玫瑰花
玫瑰花
"""

扩展:进程池执行器任务数据共享

当我们使用进程池执行器启动多进程执行任务时,如果想用数据共享,单纯 multiprocessing.Queue 进程队列并不支持。

import multiprocessing
from concurrent.futures import ProcessPoolExecutor  # 进程池执行器def task_1(q):q.put("玫瑰花")print("放完了...")def task_2(q):print(q.get())print("取到了")if __name__ == '__main__':q = multiprocessing.Queue()with ProcessPoolExecutor(max_workers=2) as pool:pool.submit(task_1,q)pool.submit(task_2,q)# ==== 执行结果 ==== # 阻塞住""""""

这个时候我们需要用到 multiprocessing 中的 Manager() 中的 Queue 。

from multiprocessing import Manager
from concurrent.futures import ProcessPoolExecutor  # 进程池执行器def task_1(q):q.put("玫瑰花")print("放完了...")def task_2(q):print(q.get())print("取到了")if __name__ == '__main__':q = Manager().Queue()with ProcessPoolExecutor(max_workers=2) as pool:pool.submit(task_1,q)pool.submit(task_2,q)# ==== 执行结果 ==== # 成功"""
放完了...
玫瑰花
取到了
"""

Python线程池与进程池相关推荐

  1. Python线程池、进程池的介绍与使用

    文章目录 1.问题背景 2.单线程→\rightarrow→多线程→\rightarrow→线程池 2.1单线程简介 2.2多线程简介 2.3线程池介绍 2.3.1复用线程 2.3.2线程池的使用 3 ...

  2. python线程池操作_python线程池和进程池

    线程池和进程池 开局来张图 使用线程池的好处 1.提升性能:因为减去了大量新建.终止线程的开销,重用了线程资源 2.使用场景:适合处理突发性大量请求或需要大量线程完成任务.但实际任务处理时间较短 3. ...

  3. python进程池_python进程池剖析(一)

    python中两个常用来处理进程的模块分别是subprocess和multiprocessing,其中subprocess通常用于执行外部程序,比如一些第三方应用程序,而不是Python程序.如果需要 ...

  4. 池技术:连接池,线程池,内存池,进程池等汇总分析

    引言 在软件开发中,经常会遇到需要频繁创建和销毁某些资源的情况.这些资源可能是内存.线程.数据库连接等.频繁地创建和销毁资源可能导致性能下降和资源浪费.为了解决这些问题,软件开发者设计了一种称为&qu ...

  5. 5,线程池,进程池,协程,IO模型

    今日内容: 1,线程池 2,进程池 3,协程 4,IO 模型 服务端要满足这三个条件: 1,24小时不间断的提供服务 2,能够支持高并发 3,要有固定的IP地址和端口在服务端这个地方会出现阻塞态情况: ...

  6. Python并发编程之进程池

    Python并发编程之进程池 一.进程池简介 二.进程池代码实例 一.进程池简介 可以用Pool类来创建进程池,可以把各种数据处理任务都提交给进程池.进程池提供的功能有点类似于列表解析和功能性编程操作 ...

  7. 内存池、进程池、线程池

    池的概念 由于服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是以空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.这就是池的概念.池是 ...

  8. python多线程,多进程,线程池,进程池

    https://blog.csdn.net/somezz/article/details/80963760 python 多线程 线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单 ...

  9. Python 多进程笔记 — 启动进程的方式、守护进程、进程间通信、进程池、进程池之间通信、多进程生产消费模型

    1 面向过程启动多进程 Python 操作进程的类都定义在 multiprocessing 模块,该模块提供了一个 Process 类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另 ...

最新文章

  1. java 语义_Java文件合并变得语义化
  2. process.cwd __dirname __filename 区别
  3. anaconda降级python_anaconda python更换清华源
  4. [招聘]期待您的加盟,与博客园一起成长
  5. 飞控计算机的作用,自动飞控计算机测试系统
  6. ​PHP现在不好找工作是真的吗?
  7. 使用Python仿真波的叠加
  8. Axure 灯箱效果
  9. Sothink Logo Maker 4.2.4254 中文绿色便携版(LOGO设计工具)
  10. 删除的win10应用商店怎么恢复
  11. C++ Primer 笔记
  12. Android EditText设置弹出数字输入法键盘
  13. 微星GL62M 7RDX笔记本矫正屏幕色彩
  14. 让用户输入一个三位数(若不是三位数则提示错误),判断该数是否是水仙花数。(水仙花数:每一位上的数字的立方和,等于该数本身)
  15. 哈尔滨苹果手机iphone不开机维修
  16. 个人家用nas_希捷个人云评测:家用NAS中的佼佼者
  17. 解决 docker 中 zsh: command not found: jupyterlab 问题
  18. 大数据的主要特征是什么?
  19. Word文档怎么删除html标签,word怎么删除一整页的两种方法
  20. 江苏具有计算机博士点的大学排名,不愧为高教强省, 江苏27所大学拥有博士点, 有你母校么...

热门文章

  1. ue4-摄像机动画Matinee(多图慎入)
  2. 仓库和门店移动出入库 手持移动终端 pda 扫描打印于一身 扫描打印小票和不干胶标签纸
  3. 变态级JAVA程序员面试32问(附答案)(转载)
  4. js原生ajax写法
  5. javascript将table的td变为可编辑的input,实现表格动态编辑(带示例版)
  6. 【005】Nginx学习笔记-Nginx真实IP
  7. Ssd202 FUART 测试环境搭建
  8. 华为手机修改默认桌面
  9. [Wi-Fi抓包篇]3. WireShark ——抓wlan口包的方法
  10. 【HTML 教程系列第 19 篇】HTML 表格中的行合并与列合并