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

Python模块中多线程调用worker()函数是否线程安全?

线程安全疑问:模块级变量的延迟初始化

我在Python模块里写了类似这样的代码:

_x = None 
def set_x(): 
    global _x 
    _x = # some value (will always be the same) 
def worker(): 
    if _x is None: 
        set_x() 

模块的客户端会创建多个线程调用worker(),想请教下这种写法线程安全吗?

补充几个背景信息:

  • _x在模块其他地方都是只读使用,一旦设置完成就不会再修改
  • 我知道可以把_x封装到类里,让线程在栈上创建实例来实现线程安全,但现在基于类的方案不可行(比如模块是单例模式,set_x()的执行成本很高,我希望尽可能少地触发这个初始化操作)

答案:这种实现并不线程安全

问题出在哪?

这是典型的「检查-然后-执行」竞态条件:

  • 假设有两个线程几乎同时进入worker(),它们都会检测到_x is None
  • 结果就是两个线程都会调用set_x()——哪怕最终_x的值是一样的,但你原本希望这个高成本的初始化只执行一次,现在却被重复触发了
  • 退一步说,就算set_x()只是简单的赋值(Python的全局变量赋值是原子操作),重复执行也会浪费资源;如果set_x()里还有其他非原子的逻辑(比如读取配置文件、调用外部接口),那还可能引发更复杂的线程安全问题

怎么修复?

给初始化过程加个锁就行,用Python的threading.Lock就能解决。这里推荐双重检查锁定的写法,既能保证线程安全,又能避免每次调用worker()都加锁的性能损耗:

修改后的代码示例:

import threading

_x = None
# 初始化锁,模块级别的锁所有线程共享
_x_init_lock = threading.Lock()

def set_x():
    global _x
    _x = # 你的初始化值(始终不变)

def worker():
    global _x
    # 第一次检查:如果已经初始化过,直接跳过锁逻辑
    if _x is None:
        with _x_init_lock:
            # 第二次检查:等待锁的过程中,可能其他线程已经完成了初始化
            if _x is None:
                set_x()

这种写法的好处是:只有第一次初始化的时候会用到锁,之后_x已经有值,所有线程都会直接跳过锁的逻辑,性能影响极小。

额外小提示

  • 如果你的Python版本是3.9+,其实functools.lru_cache(maxsize=None)可以把set_x()变成一个只执行一次的函数,但要注意如果set_x()有副作用,这种方式的线程安全依赖Python版本;相比之下,手动加锁的方式更透明、更稳妥
  • 模块级变量是进程内共享的,所以这里用threading.Lock足够;如果是多进程场景,那得换成multiprocessing.Lock,不过你这里是多线程场景,不用考虑这个

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:54:38