You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 22:15:08