python 多线程编程(一)
这一篇主要记录线程池的基本知识和用法。
一般计算(CPU)密集型任务适合多进程,IO密集型任务适合多线程;
concurrent.futures
异步执行可以由 ThreadPoolExecutor 使用线程或由 ProcessPoolExecutor 使用单独的进程来实现。 两者都是实现抽象类 Executor 定义的接口。
Executor 对象
class concurrent.futures.Executor:抽象类提供异步执行调用方法。要通过它的子类调用,而不是直接调用。
- submit(fn, /, *args, **kwargs): 调度可调用对象 fn,以 fn(*args, **kwargs) 方式执行并返回一个代表该可调用对象的执行的 Future 对象。
- map(fn, *iterables, timeout=None, chunksize=1):返回一个将 fn 应用于 iterable 的每一项,并产生其结果的迭代器。fn 是异步执行的并且可以并发对 fn 的多个调用。
- shutdown(wait=True, *, cancel_futures=False): 当待执行的 future 对象完成执行后向执行者发送信号,它就会释放正在使用的任何资源。如果 wait 为 True 则此方法只有在所有待执行的 future 对象完成执行且释放已分配的资源后才会返回;如果 wait 为 False,方法立即返回,所有待执行的 future 对象完成执行后会释放已分配的资源。
Future对象
Future 类将可调用对象封装为异步执行。Future 实例由 Executor.submit() 创建。
ThreadPoolExecutor
ThreadPoolExecutor 是 Executor 的子类,它使用线程池来异步执行调用。
class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix=‘’, initializer=None, initargs=())
Executor 子类使用最多 max_workers 个线程的线程池来异步执行调用。
代码示例
首先,先导入concurrent.futures库,然后定义一个模拟函数;接下来使用ThreadPoolExecutor创建一个线程池,随后创建一个包含多个任务的任务队列task_queue,然后用submit方法将每个任务提交到线程池中执行。最后调用shutdown方法等待所有任务完成并关闭线程池。
import concurrent.futures
# 模拟的任务函数
def task_function(task):
print(f"Processing task: {task}")
# 创建线程池
with concurrent.futures.ThreadPoolExecutor() as executor:
# 创建一个任务队列
task_queue = ["Task 1", "Task 2", "Task 3", "Task 4", "Task 5"]
# 提交任务到线程池
for task in task_queue:
executor.submit(task_function, task)
# 等待所有任务完成
executor.shutdown()
上述的方法还可以使用map来处理队列中的多个任务。
import concurrent.futures
# 模拟的任务函数
def task_function(task):
print(f"Processing task: {task}")
return task.upper()
# 创建线程池
with concurrent.futures.ThreadPoolExecutor() as executor:
# 创建一个任务队列
task_queue = ["Task 1", "Task 2", "Task 3", "Task 4", "Task 5"]
# 使用map方法处理任务队列
results = executor.map(task_function, task_queue)
# 获取任务的执行结果
for result in results:
print(f"Task result: {result}")
ProcessPoolExecutor
ProcessPoolExecutor 类是 Executor 的子类,它使用进程池来异步地执行调用。 ProcessPoolExecutor 会使用 multiprocessing 模块,这允许它绕过 全局解释器锁 但也意味着只可以处理和返回可封存的对象。
__main__ 模块必须可以被工作者子进程导入。这意味着ProcessPoolExecutor不可以工作在交互式解释器中。
class concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=(), max_tasks_per_child=None)
异步地执行调用的 Executor 子类使用最多 max_workers 个进程的进程池。 如果max_workers为None或未给出,它将默认为机器的处理器个数。 如果 max_workers小于等于0,则将引发ValueError。
示例
对视频文件想要并行处理,同时还得保证处理后的帧顺序不变。使用submit方法可能会出现帧乱序的情况,可以使用map方法。map方法会按照任务在任务队列中的顺序返回结果。所以,先按顺序读取原视频的所有帧,将其放到一个队列中,然后作为map的参数传进去。
import cv2
import concurrent.futures
import queue
import numpy as np
# 视频帧处理函数
def process_frame(frame):
# 将图像转为灰度图
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
return frame
# 读取视频文件
cap = cv2.VideoCapture('your_video_path.mp4')
frames = []
while True:
ret, frame =cap.read()
if not ret:
break
frames.append(frame)
# 获取视频的帧率、帧大小和总帧数
fps = cap.get(cv2.CAP_PROP_FPS)
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(fps, frame_width, frame_height, frame_count)
# 创建输出视频文件
fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 根据实际情况选择合适的编码器
out = cv2.VideoWriter('your_new_video_path.mp4', fourcc, fps, (frame_width, frame_height), isColor=False) #灰度图视频
# 创建线程池和队列
with concurrent.futures.ThreadPoolExecutor() as executor:
feature_results = executor.map(process_frame, frames)
for result in feature_results:
# print(type(result), result.shape, result)
out.write(result)
cap.release()
out.release() # 释放输出视频文件句柄