池
【1】概念
-
池是用来保证计算机硬件安全的情况下,最大限度的利用计算机资源,降低了程序的运行效率,但是保证了计算机硬件的安全
-
池(Pool)的概念在计算机科学和软件工程中常被用于资源管理,尤其是在多线程或并发编程中。池是一种管理和优化资源分配的机制,它事先分配一定数量的资源(如线程、数据库连接、对象等),并在需要时重用这些资源,而不是每次都创建和销毁。
【2】类型
- 线程池(Thread Pool):线程池维护一组预先创建的线程,这些线程可以执行多个任务。当一个任务提交给线程池时,线程池会尝试使用一个空闲的线程来执行该任务。使用线程池可以减少因为频繁创建和销毁线程而产生的开销,提高响应速度,同时限制并发线程的数量,防止资源过度使用。
- 进程池(Process Pool):与线程池类似,进程池维护一组预先创建的进程。这在并行处理和分布式计算中非常有用,尤其是当每个任务需要大量计算资源时。
- 数据库连接池(Database Connection Pool):在数据库应用中,建立数据库连接是一个资源密集和时间消耗的操作。数据库连接池预先创建一定数量的数据库连接,并在需要时重用这些连接,提高了数据库操作的效率。
- 对象池(Object Pool):对象池用于管理对象的创建和分配。它维护一组已经初始化的对象。当需要一个新对象时,可以从池中获取,使用后再放回池中,而不是销毁。
【3】优势
- 提高性能:通过减少创建和销毁资源的次数,减少系统开销。
- 资源复用:已分配的资源可以被多次重用,提高资源利用率。
- 控制上限:限制同时活跃的资源数量,防止因过多资源同时使用而耗尽系统资源。
线程池
【1】导入方法
from concurrent.futures import ThreadPoolExecutor
【2】使用方法
- 先实例化一个线程池对象
t_pool = ThreadPoolExecutor(5)
- 这里的参数可以自己填,不填的话,默认就是电脑cpu个数乘5
t_list = [t_pool.submit(task, i) for i in range(1, 51)]
通过submit()
方法向线程池中提交任务submit(task, i)
第一个参数是函数名,第二个参数是函数的参数- 通过
result()
方法可以得到线程池里的线程的返回值 shutdown()
方法可以使线程池关闭,不再处理新的任务,但是会处理已经提交给线程池内部正在运行,以及在队列中还未运行的线程
【3】进房间洗脚例子
from concurrent.futures import ThreadPoolExecutor
import time
def task(i):
print(f'客户{i}来到了房间洗脚')
time.sleep(2)
return f'客户{i}来洗脚啦'
if __name__ == '__main__':
t_pool = ThreadPoolExecutor(5)
t_list = [t_pool.submit(task, i) for i in range(1, 51)]
t_pool.shutdown()
for t in t_list:
print(t.result())
- 首先将任务添加到线程池,然后再关闭线程池,等待所有线程起来之后,最后再一起输出结果
- 也可以使用
with ThreadPoolExecutor(3) as p_pool:
自动执行shutdown()
方法
进程池
【1】导入方法
from concurrent.futures import ProcessPoolExecutor
【2】使用方法
- 进程池的使用方法和线程池基本一致
- 但是如果不给进程池传入参数时,默认值会是当前电脑cpu的个数
【3】池里面的进程或者线程是不会变的
- 上面的线程池不太好说明
- 但是进程池就可以很好的印证这一点
- 还是一样的洗脚例子
from concurrent.futures import ProcessPoolExecutor
import os
import time
def task(i):
pid = os.getpid()
time.sleep(1)
print(f'客户{i}进入房间洗脚----客户的手牌号为{pid}')
if __name__ == '__main__':
p_pool = ProcessPoolExecutor(3)
p_list = [p_pool.submit(task, i) for i in range(1, 21)]
- 打印 出来观察结果,可以发现进程池里面的进程ID是不变的,永远都是那么几个
异步回调机制
【1】介绍
- 异步回调机制是一种编程模式,它允许程序在执行长时间运行的任务时继续执行其他代码,并在这些长时间运行的任务完成后得到通知。
- 结合上面的例子,我们想要拿到线程的返回值,但是拿到返回值就需要1秒的阻塞,用了异步回调函数之后,就可以实现线程的并发,等到拿到了函数的返回值,再调回来拿结果。
【2】异步回调的优势
-
非阻塞行为:主程序可以继续执行其他任务,而不必等待长时间运行的任务完成。
-
简化代码结构:回调机制允许我们将后续处理逻辑清晰地与长时间运行的任务分离。
-
提高响应性:在用户界面或网络应用中,异步回调能够提高程序的响应性,因为主线程不会被长时间运行的任务阻塞。
-
异步回调在现代编程中非常普遍,特别是在处理I/O操作(如文件读写、网络请求)或其他需要等待的任务时。
【3】例子
from concurrent.futures import ProcessPoolExecutor
import os
import time
def task(i):
pid = os.getpid()
time.sleep(2)
print(f'客户{i}进入房间洗脚----客户的手牌号为{pid}')
return f'客户{i}来洗脚啦'
def result(future):
print(future.result())
if __name__ == '__main__':
p_pool = ProcessPoolExecutor(3)
p_list = [p_pool.submit(task, i).add_done_callback(result) for i in range(1, 11)]
add_done_callback()
调用这个方法会自动将p_pool
对象作为第一个参数传入,而这个对象就有一个result()
方法可以拿到返回值- 定义了一个
result
函数,参数是future对象,也就是p_pool
对象,函数内容就是输出p_pool
对象的返回值,然后再将这个函数地址作为add_done_callback()
的参数传入