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

线程Timer定时调用函数失效问题及MUTEX同步实现咨询

解决你的Timer失效与线程同步问题

嘿,先帮你揪出Timer失效的核心原因:你在创建Timer的时候直接写了storeIntoSQL(names,unknown_encoding),这相当于立刻执行了这个存储函数,而不是把函数本身传给Timer去延迟执行。Timer需要的是函数对象,以及对应的参数,不是函数执行后的结果哦。

一、先把Timer给修好

把你的Timer代码改成下面这样就正常了:

from threading import Timer

# 正确传递函数对象,用args传位置参数
timer = Timer(60, storeIntoSQL, args=(names, unknown_encoding))
timer.start()

这样Timer才会乖乖等60秒再调用存储函数,而不是一上来就执行。

不过这里还有个小问题:你硬设60秒延迟不一定匹配实际的学习耗时啊?万一学习只花了10秒,那还要等50秒;如果学习花了70秒,Timer提前触发了就麻烦了。更合理的做法是等学习过程完全结束后,再启动存储的线程,而不是靠固定时间猜。

二、更靠谱的异步执行方案(替代Timer)

既然你不想阻塞主线程,又要保证学习完再存储,直接用threading.Thread来处理每个样本的学习+存储就好,或者把存储逻辑放到学习完成后的子线程里:

方案1:每个样本单独开线程处理

这样主线程可以继续跑其他任务,完全不会被阻塞:

import threading

def process_and_store(name_str, image_data, current_id):
    # 先完成学习过程(就是你原来的face_recognition那段逻辑)
    img_path = f'images/Output{current_id}.jpg'
    with open(img_path, "wb") as output_file:
        output_file.write(image_data)
    unknown_image = face_recognition.load_image_file(img_path)
    unknown_encoding = face_recognition.face_encodings(unknown_image)[0]
    
    # 学习完直接执行存储,不用等固定时间
    storeIntoSQL(name_str, unknown_encoding)

# 主逻辑里调用
with myConnection:
    cur = myConnection.cursor()
    cur.execute("Select name, image FROM images WHERE state = 1")
    rows = cur.fetchall()
    id = 0
    for row in rows:
        print("%s, %s" % (row[0], row[1]))
        # 给每个样本开个线程处理,把当前id传进去避免全局变量冲突
        thread = threading.Thread(target=process_and_store, args=(row[0], row[1], id))
        thread.start()
        id += 1

这样每个样本的学习和存储都在独立线程里完成,主线程该干嘛干嘛,完全不耽误。

三、MUTEX(互斥锁)的具体实现

你提到的MUTEX其实就是互斥锁,用来解决多个线程同时操作数据库时的数据竞争问题——比如两个线程同时写数据库,可能会导致数据错乱。实现起来很简单:

第一步:先定义一个全局锁

在代码开头加一行:

import threading
# 创建全局互斥锁,所有操作数据库的线程都共用这个锁
db_lock = threading.Lock()

第二步:在存储函数里用锁保护数据库操作

修改你的storeIntoSQL函数,把数据库操作的代码用锁包起来:

def storeIntoSQL(name, unknown_face_encoding):
    print('i am printing')
    # 用with语句自动获取和释放锁,避免忘记释放导致死锁
    with db_lock:
        # 连接数据库
        con = lite.connect('users2.db')
        with con:
            cur = con.cursor()
            # 获取新的用户ID
            cur.execute("SELECT DISTINCT id FROM Users ")
            rows = cur.fetchall()
            newId = len(rows)+1
            # 写入数据
            query = "INSERT INTO Users VALUES (?,?,?)"
            cur.executemany(query, [(newId, name, r,) for r in unknown_face_encoding])
    # 锁会自动释放,数据库连接也会因为with语句自动关闭

这样一来,同一时间只有一个线程能执行数据库操作,就不会出现数据冲突了。

四、更省心的替代方案:线程池

如果你觉得手动管理线程麻烦,可以用concurrent.futures.ThreadPoolExecutor来管理线程池,它会自动帮你处理线程的创建和销毁:

from concurrent.futures import ThreadPoolExecutor

# 定义线程池大小,比如最多同时跑5个线程
executor = ThreadPoolExecutor(max_workers=5)

# 主逻辑里提交任务
with myConnection:
    cur = myConnection.cursor()
    cur.execute("Select name, image FROM images WHERE state = 1")
    rows = cur.fetchall()
    id = 0
    for row in rows:
        print("%s, %s" % (row[0], row[1]))
        # 把任务提交给线程池
        executor.submit(process_and_store, row[0], row[1], id)
        id += 1

这个方式更简洁,适合处理批量的任务。

最后再提几个小注意点:

  • 尽量避免用全局变量传递id,像上面那样直接把当前id作为参数传给线程函数,能减少多线程下的变量冲突。
  • 确保face_recognition库是线程安全的,如果它内部有全局状态,可能也需要加锁保护。
  • 数据库连接最好每个线程单独创建,或者用连接池,别让多个线程复用同一个连接,容易出问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:19:50