Python线程池与进程池
Python线程池与进程池
前言
前面我们已经将线程并发编程与进程并行编程全部摸了个透,其实我第一次学习他们的时候感觉非常困难甚至是吃力。因为概念实在是太多了,各种锁,数据共享同步,各种方法等等让人十分头痛。所以这边要告诉你一个好消息,前面的所有学习的知识点其实都是为本章知识点做铺垫,在学习了本章节的内容后关于如何使用多线程并发与多进程并行就采取本章节中介绍的方式即可。
这里要介绍一点与之前内容不同的地方,即如果使用队列进行由进程池创建的进程之间数据共享的话不管是multiprocessing
模块下的Queue
还是queue
模块下的Queue
都不能为进程池中所创建的进程进行数据共享,我们需要用到另一个队列即multiprocessing.Manager()
中的Queue
。当然这个我也会在下面介绍到。那么开始学习吧!
执行器
最早期的Python2中是没有线程池这一概念的,只有进程池。直到Python3的出现才引入了线程池,其实关于他们的使用都是非常简单,而且接口也是高度统一甚至说一模一样的。而线程池与进程池的作用即是为了让我们能够更加便捷的管理线程或进程。
我们先说一下,如果需要使用线程池或进程池,需要导入模块concurrent.futures
。
from concurrent.futures import ThreadPoolExecutor
# 线程池执行器
from concurrent.futures import ProcessPoolExecutor
# 进程池执行器
这里介绍一下,关于线程池或者进程池创建出的线程与进程与我们使用multiprocessing
模块或者threading
模块中创建的线程或进程有什么区别。我们以多线程为例:
import threading def 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 没有指定或为 None ,那么等待时间就没有限制。 |
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线程池与进程池的更多相关文章
- Python并发复习4- concurrent.futures模块(线程池和进程池)
Python标准库为我们提供了threading(多线程模块)和multiprocessing(多进程模块).从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提 ...
- python自带的进程池及线程池
进程池 """ python自带的进程池 """ from multiprocessing import Pool from time im ...
- Python并发编程之进程池与线程池
一.进程池与线程池 python标准模块concurrent.futures(并发未来) 1.concurrent.futures模块是用来创建并行的任务,提供了更高级别的接口,为了异步执行调用 2. ...
- python GIL锁、进程池与线程池、同步异步
一.GIL全局解释器锁 全局解释器锁 在CPython中,全局解释器锁(GIL)是一个互斥锁,它可以防止多个本机线程同时执行Python代码.之所以需要这个锁,主要是因为CPython的内存管理不是线 ...
- Python学习之GIL&进程池/线程池
8.6 GIL锁** Global interpreter Lock 全局解释器锁 实际就是一把解释器级的互斥锁 In CPython, the global interpreter lock, or ...
- python之线程池和进程池
线程池和进程池 一.池的概念 池是用来保证计算机硬件安全的情况下最大限度的利用计算机 它降低了程序的运行效率但是保证了计算机硬件的安全从而让你写的程序能够正常运行 ''' 无论是开设进程也好还是开设线 ...
- 协程与concurent.furtrue实现线程池与进程池
1concurent.furtrue实现线程池与进程池 2协程 1concurent.furtrue实现线程池与进程池 实现进程池 #进程池 from concurrent.futures impor ...
- 并发编程:GIL,线程池,进程池,阻塞,非阻塞,同步,异步
一 GIL(global interpreter lock) GIL中文叫全局解释器锁,我们执行一个文件会产生一个进程,那么我们知道进程不是真正的执行单位,而是资源单位,所以进程中放有解释器(cpy ...
- 多进程 multiprocessing 多线程Threading 线程池和进程池concurrent.futures
multiprocessing.procsess 定义一个函数 def func():pass 在if __name__=="__main__":中实例化 p = process( ...
随机推荐
- Java实现 LeetCode 54 螺旋矩阵
54. 螺旋矩阵 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素. 示例 1: 输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], ...
- java实现指数问题
3^n mod 19 求n次幂,对19取模 ================ (3 * 3) * (3 * 3) * 3 public class A { // 分治 public static in ...
- Java实现 蓝桥杯 历届试题 国王的烦恼
问题描述 C国由n个小岛组成,为了方便小岛之间联络,C国在小岛间建立了m座大桥,每座大桥连接两座小岛.两个小岛间可能存在多座桥连接.然而,由于海水冲刷,有一些大桥面临着不能使用的危险. 如果两个小岛间 ...
- java实现第七届蓝桥杯压缩变换
压缩变换 压缩变换 小明最近在研究压缩算法. 他知道,压缩的时候如果能够使得数值很小,就能通过熵编码得到较高的压缩比. 然而,要使数值很小是一个挑战. 最近,小明需要压缩一些正整数的序列,这些序列的特 ...
- 一张图说清楚Flink水印和Lateness
真传一句话,假言万卷书. Flink使用窗口与水印处理乱序问题,使用Latenss处理延迟问题,二者混合使用可以满足很多复杂场景的需求 当Watermark大于窗口结束时间时,会触发窗口内的元素进行计 ...
- CSS清除浮动&内容居中&文字溢出
学习! 1.CSS清除浮动的方法 (1)添加标签清除浮动: 在浮动元素结尾处,并列的添加标签<div style="clear:both;"></div>. ...
- 第03组团队Git现场编程实战
1.组员职责分工 张逸杰:复制监督整个编程任务的进程以及协助组员编程 黄智锋.刘汪洋:负责UI设计 苏凯婷.鲍冰如:爬取数据并负责测评出福州最受欢迎的商圈 陈荣杰.杨锦镔:爬取数据并负责测评出福州人均 ...
- Tomcat/ WebSphere/WebLogic的作用和特点
作用: Tomcat:目前应用非常广泛的免费web服务器,支持部分j2ee. WebSphere:是IBM集成软件平台.可做web服务器,WebSphere提供了可靠.灵活和健壮的集成软件. Webl ...
- 了解JSON Web令牌(JWT)
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. (一) 跨域身份验证 Internet服务无法与用户身份验证分开. 用户向服务器发送用户名和密码. 验证服务器后,相关数据( ...
- Centos 各个版本的下载源
China Alibaba Cloud Computing http://mirrors.aliyun.com/centos/ China Beijing Institute of Technolog ...