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

Python线程场景下如何不重新绑定变量替换集合?

解决线程引用集合时的内容更新问题

要实现不重新绑定变量、直接更新原集合内容的需求,核心是修改原集合对象的内部元素而非替换变量指向的对象,同时必须处理多线程访问的安全性问题,具体方案如下:

关键修改点

  1. 替换集合内容的正确方式:不要用symmetric_difference_update(它只会做差集更新),而是先调用clear()清空原集合,再用update()把新集合的元素全部加入,这样原集合对象的引用不变,线程能获取到最新内容。
  2. 线程安全保护:多线程同时读写集合会引发竞态条件,需要用threading.Lock对所有集合的读写操作加锁。
  3. 包装存活判断函数:不能直接传递terms.__contains__,要把判断逻辑包装成带锁的函数,确保线程访问集合时是线程安全的。

修改后的完整代码

import threading
import time
from typing import Callable

from loguru import logger


class MonitorProduct:
    def __init__(self, term: str, is_alive: Callable[[str], bool]) -> None:
        self.is_alive = is_alive
        self.term = term

    def do_request(self) -> None:
        time.sleep(.1)
        while True:
            logger.info(f'Checking {self.term}')
            if not self.is_alive(self.term):
                logger.info(f'Deleting term from monitoring: "{self.term}"')
                return
            time.sleep(5)


# mocked database
def database_terms() -> set[str]:
    return {
        'hello world',
        'python 3',
        'world',
        'wth',
    }


def database_terms_2() -> set[str]:
    return {
        'what am I doing wrong',
    }


def main() -> None:
    terms: set[str] = set()
    lock = threading.Lock()  # 用于保护集合的读写锁

    # 带锁的存活判断函数
    def is_term_alive(term: str) -> bool:
        with lock:
            return term in terms

    while True:
        db_terms = database_terms()
        with lock:
            diff = db_terms - terms
            terms.clear()
            terms.update(db_terms)

        for url in diff:
            logger.info(f'Starting URL: {url}')
            threading.Thread(
                target=MonitorProduct(url, is_term_alive).do_request
            ).start()

        time.sleep(2)

        # ----------------------------------------------- #

        db_terms = database_terms_2()
        with lock:
            diff = db_terms - terms
            terms.clear()  # 清空原集合
            terms.update(db_terms)  # 加入新集合的所有元素

        # Start the new URLS
        for url in diff:
            logger.info(f'Starting URL 2: {url}')
            threading.Thread(
                target=MonitorProduct(url, is_term_alive).do_request
            ).start()

        time.sleep(10)


if __name__ == '__main__':
    main()

说明

  • 主线程更新集合时,通过lock确保修改操作是原子性的,避免线程读取到半更新的集合状态。
  • 线程中的存活判断通过is_term_alive函数加锁访问集合,保证每次判断的准确性。
  • 第二次调用database_terms_2()后,原terms集合被清空并替换为新元素,所有旧线程会在下一次判断时触发退出逻辑。

内容的提问来源于stack exchange,提问作者PythonNewbie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:54:18