RuntimeError: Already borrowed

该报错发生在多线程并发调用同一 tokenizer 实例时,尤其是每次调用的 padding/truncation 设置不同导致每次都会触发内部状态重配的场景。优先排查是否为同一 tokenizer 实例被多线程共享,并检查每次调用的 padding/truncation 参数是否一致。

快速结论:该报错发生在多线程并发调用同一 tokenizer 实例时,尤其是每次调用的 padding/truncation 设置不同导致每次都会触发内部状态重配的场景。优先排查是否为同一 tokenizer 实例被多线程共享,并检查每次调用的 padding/truncation 参数是否一致。

适用环境:transformers 5.5.4 和 4.57.6、Python 3.12.3、tokenizers 0.22.2(Rust 后端绑定)。

最快修复方案:使用共享的 threading.Lock 包装所有 tokenizer 调用,可消除并发错误(已验证 0/4000 错误);或确保每次调用使用完全相同的 padding/truncation 参数(已验证 0/4000 错误)。

注意事项:上述两种方案是已验证的可行方法,但并非官方修复;多线程性能会因加锁而下降。相同调用参数方案仅在当前设置匹配时跳过内部重配,并非语言级线程安全保证。

问题场景

用户在多线程环境下(如 ThreadPoolExecutor)共享同一个 AutoTokenizer 实例进行编码,且每次调用的 padding/truncation 设置不同(例如一个调用启用 padding 和 truncation,另一个调用不启用),触发并发错误。

报错原文

RuntimeError: Already borrowed

原因分析

可能原因:Rust 后端的 tokenizer 是一个共享的 PyO3 #[pyclass] 对象,内部使用类似 RefCell 的借用检查。当线程 A 执行 encode_batch 时,会释放 GIL 但持有对象的共享借用(&self);此时线程 B 执行 set_truncation_and_padding,需要获取可变借用(&mut self),与线程 A 的共享借用冲突,触发 PyO3 的运行时借用错误。

该问题并非 transformers v5 新增——在 4.57.6 上同样重现(3519/4000 错误)。由于每次调用使用不同 padding/truncation 设置,set_truncation_and_padding 的守卫判断 current != target 恒为真,导致每次调用都触发 &mut 重配,与并发编码的 &self 借用持续冲突。

环境排查

  • 确认 Python 版本(Issue 验证:3.12.3)
  • 确认 transformers 版本(Issue 验证:5.5.4 和 4.57.6 均复现)
  • 确认 tokenizers 版本(Issue 验证:0.22.2)
  • 确认是否在多线程中共享同一 tokenizer 实例
  • 确认每次调用的 padding/truncation 参数是否一致

解决步骤

  1. 方案一(已验证):在共享 tokenizer 的所有调用处加入 threading.Lock,确保同一时刻只有一个线程访问 tokenizer。
  2. 方案二(已验证):确保线程池中所有调用使用完全相同的 padding/truncation 参数(包括 max_length、padding 策略、truncation 策略),使内部守卫跳过重配。
  3. 如果业务要求每次调用参数不同,可优先尝试方案一,或为不同参数组合创建独立的 tokenizer 实例。

验证方法

运行包含 4000 次并发调用的复现脚本,统计异常数量。预期结果从数千错误降至 0 错误(如 Issue 中验证的 errors=0/4000)。可添加异常类型捕获以确认错误类型为 RuntimeError: Already borrowed

参考来源

huggingface/transformers #47085

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 19812

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注