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

pytest运行时不同线程调用threading.get_ident()返回相同ID问题

线程get_ident()返回重复ID问题说明

复现场景

复现代码如下,运行环境为Python 3.9.0、Pytest 7.1.2:

from threading import Thread, get_ident
# import time

def test_threading():
    threads = []
    ids = []

    def append_id():
        # 取消下方注释后测试可正常通过
        # time.sleep(1)
        ids.append(get_ident())

    for _ in range(5):
        thread = Thread(target=append_id)
        threads.append(thread)
        thread.start()

    for thread in threads:
        thread.join()

    assert len(set(ids)) == 5

现象:不加sleep时断言失败,提示不同线程拿到了相同的get_ident()返回值;给线程任务加上time.sleep(1)后断言正常通过。


核心根因

这个现象和pytest框架没有关系,本质是线程ID的设计规则+测试任务执行速度过快共同导致的:

threading.get_ident()返回的线程ID,仅在对应线程处于存活状态的周期内保证全局唯一。一旦线程执行完毕退出,它占用的ID会被Python运行时和操作系统回收,后续新创建的线程可以直接复用这个ID,不存在「线程ID全局永久唯一」的设计。

不加sleep失败的原因

你写的append_id函数只有一行逻辑,从线程启动到执行完退出全程仅需几微秒,速度远快于主线程循环创建5个线程的速度:

  • 主线程创建第一个线程调用start()后,这个线程瞬间完成「读取ID、追加到列表」的操作,立刻终止,占用的ID被标记为可复用状态
  • 主线程继续创建第二个线程时,操作系统直接把刚回收的ID分配给新线程
  • 最终5个线程实际是串行执行、依次退出的,从来没有同时处于存活状态,拿到的ID自然会重复,集合长度自然达不到5。

加sleep后通过的原因

给线程任务加上1秒休眠后,每个线程启动后都会保持至少1秒的存活状态,不会立刻退出:

  • 主线程循环创建、启动5个线程的总耗时远小于1秒,5个线程会在极短时间内全部进入存活状态
  • 同时存活的线程持有的ID不会被回收复用,因此每个线程拿到的ID都是唯一的,断言正常通过。

避坑提示

如果你需要给线程做跨生命周期的唯一标识,不要依赖get_ident()或者get_native_id()——这两个接口返回的都是内核层面的临时线程句柄,都会在线程退出后被回收复用。可行的方案包括:

  • 自定义线程类,在初始化阶段生成UUID作为线程的唯一标识
  • 创建线程时给name参数传入全局唯一的自定义名称,使用时通过threading.current_thread().name读取
  • 仅当你能确认所有需要区分的线程同时处于存活状态时,才可以用get_ident()做临时区分。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 10:27:18