使用ThreadPoolExecutor运行后台无限循环时如何异步持续更新共享变量?
解决方案
核心问题分析
- 两个工作函数的
return语句位于while True循环内部,第一次循环执行完成后就会直接终止函数,无法持续运行 Future.result()是阻塞调用,会等待线程执行结束才返回,完全不符合异步持续更新的需求- 直接使用全局变量会有线程竞态问题,需要用线程安全的方式存储共享状态
实现方案
用带线程锁的共享状态容器替代全局变量和return返回值的方式,两个工作线程持续运行,每轮计算完成后更新容器内的对应值,其他逻辑随时可以读取容器内的最新值。
这种方式不需要修改整体的线程池使用逻辑,也能保证线程安全,不需要依赖全局变量。
完整代码实现
首先导入依赖,定义线程安全的共享容器:
import concurrent.futures import threading import time import cv2 import numpy as np from PIL import ImageGrab from win32gui import FindWindow, GetWindowRect # 替换为你自己的模板匹配函数导入路径 from your_module import check_template, templates, WINDOW_NAME # 线程安全的共享状态容器,无需全局变量 class SharedState: def __init__(self): self._lock = threading.Lock() self.latest_image = None self.latest_state = None def update_image(self, image): with self._lock: self.latest_image = image def get_image(self): with self._lock: return self.latest_image def update_state(self, state): with self._lock: self.latest_state = state def get_state(self): with self._lock: return self.latest_state
修改imageWindow函数,去掉循环内的return,将结果更新到共享容器:
def imageWindow(shared_state: SharedState): while True: window = FindWindow(None, "Webull Desktop") rect = GetWindowRect(window) image = ImageGrab.grab(rect) image_arr = np.array(image) cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL) cv2.imshow(WINDOW_NAME, image_arr) key = cv2.waitKey(1) if key > 0: break # 更新共享状态,不直接return shared_state.update_image(image)
修改weighTemplates函数,每次取最新图片计算,将结果更新到共享容器:
def weighTemplates(shared_state: SharedState): while True: # 拿最新的图片,没有拿到就跳过本轮计算 image = shared_state.get_image() if image is None: time.sleep(0.01) continue weights = [] for i in range(len(templates)): threshold = templates[i]['threshold'] template = templates[i]['image'] certainty = check_template(image, template, True) certainty = certainty * 100 templates[i]['results'] = certainty weights.append(certainty) max_index = weights.index(max(weights)) # 更新共享状态,不直接return shared_state.update_state(templates[max_index]['status']) # 可根据需求加延迟控制计算频率,降低CPU占用 time.sleep(0.05)
调用逻辑,启动线程后主线程可以随时读取最新值执行后续操作:
if __name__ == "__main__": shared_state = SharedState() # 不用with上下文管理器,避免阻塞等待线程结束 executor = concurrent.futures.ThreadPoolExecutor(max_workers=2) # 启动两个异步工作线程 executor.submit(imageWindow, shared_state) executor.submit(weighTemplates, shared_state) # 主线程业务逻辑,随时读取最新值即可 while True: current_image = shared_state.get_image() current_state = shared_state.get_state() if current_image is not None and current_state is not None: # 在这里执行你的后续业务逻辑 print(f"当前检测状态:{current_state}") # 退出逻辑 if cv2.waitKey(100) > 0: break # 退出前释放资源 cv2.destroyAllWindows() executor.shutdown(wait=False)
注意事项
- 部分操作系统的CV2窗口渲染必须在主线程执行,如果出现窗口卡住无响应的情况,把
imageWindow里的imshow、waitKey相关代码移到主线程的循环里即可。 - 可根据实际需求调整两个工作线程内的延迟参数,平衡实时性和CPU占用率。
内容的提问来源于stack exchange,提问作者addohm
相关产品推荐
相关产品推荐

