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
相关产品推荐
相关产品推荐

