python - getattr/setattr/hasattr/delattr 线程安全吗?

标签 python multithreading

看这个单例实现:

if not hasattr(Singleton, "_instance"):                                    
    with Singleton._instance_lock:                                         
        if not hasattr(Singleton, "_instance"):                            
            Singleton._instance = Singleton()                                 
return Singleton._instance                                      

似乎“Singleton._instance = ..”(类似于setattr)和hasattr 是原子的。 或者 hasattr 不会因为 setattr 而导致崩溃。

但我找不到任何支持以上“似乎”的东西。

最佳答案

通常,前提是您对其调用操作的对象未实现 __getattr__ , __delattr____setattr__ hooks in python,那么是的,hasattr, getattr, delattr and setattr 都是原子操作。

就 Python 线程而言,任何单独的字节码都是原子操作。 Python 评估循环在解释操作码时获取全局解释器锁 (GIL)。

您需要查看字节码以了解边界所在:

>>> def foo():
...     if not hasattr(Singleton, "_instance"):
...         with Singleton._instance_lock:
...             if not hasattr(Singleton, "_instance"):
...                 Singleton._instance = Singleton()
...     return Singleton._instance
... 
>>> dis.dis(foo)
  2           0 LOAD_GLOBAL              0 (hasattr)
              3 LOAD_GLOBAL              1 (Singleton)
              6 LOAD_CONST               1 ('_instance')
              9 CALL_FUNCTION            2
             12 POP_JUMP_IF_TRUE        64

  3          15 LOAD_GLOBAL              1 (Singleton)
             18 LOAD_ATTR                2 (_instance_lock)
             21 SETUP_WITH              35 (to 59)
             24 POP_TOP             

  4          25 LOAD_GLOBAL              0 (hasattr)
             28 LOAD_GLOBAL              1 (Singleton)
             31 LOAD_CONST               1 ('_instance')
             34 CALL_FUNCTION            2
             37 POP_JUMP_IF_TRUE        55

  5          40 LOAD_GLOBAL              1 (Singleton)
             43 CALL_FUNCTION            0
             46 LOAD_GLOBAL              1 (Singleton)
             49 STORE_ATTR               3 (_instance)
             52 JUMP_FORWARD             0 (to 55)
        >>   55 POP_BLOCK           
             56 LOAD_CONST               0 (None)
        >>   59 WITH_CLEANUP        
             60 END_FINALLY         
             61 JUMP_FORWARD             0 (to 64)

  6     >>   64 LOAD_GLOBAL              1 (Singleton)
             67 LOAD_ATTR                3 (_instance)
             70 RETURN_VALUE        

故事并没有就此结束; hasattr 使用 getattr()(测试异常),后者又可以调用 Python __getattr__ Hook 。同样,STORE_ATTR 操作码最终可能会调用 python __setattr__ Hook 实现。在这两种情况下,GIL 都会被再次释放。

对于默认实现(Singleton 不实现这些钩子(Hook)),操作是原子的,因为 Python C 代码处理整个操作而不会回退到 Python,因此评估循环(GIL 可能被释放)并再次锁定另一个线程)。

当然,您仍然可以处理在 object protocol operations 期间释放锁的自定义 C 库.那将是一件不寻常的事情。

关于python - getattr/setattr/hasattr/delattr 线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16872210/

相关文章:

java - 如何获取 java.util.concurrent.locks.Lock 的锁定/解锁状态

java - 使用条件 TimerTask 不会终止线程

python - nlargest on groupby 具有多索引和多个聚合列

python - 将 -profile 参数添加到 python selenium 选项时,Firefox 连接被拒绝

python - 迭代sqlalchemy模型的定义列的方法?

Java "synchronized"在对象类或调用类中

c# - 我使用信号量错了吗?

python - 将 Python 方法转换为 C 扩展的好处?

python - 当发送给它的查询太长时,uWSGI 似乎会中断

c++ - 启动 std::thread 时无法将(函数指针)左值绑定(bind)到(函数指针)右值?