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

hel*_*.co 3 python multithreading

看到这个Singleton实现:

if not hasattr(Singleton, "_instance"):                                    
    with Singleton._instance_lock:                                         
        if not hasattr(Singleton, "_instance"):                            
            Singleton._instance = Singleton()                                 
return Singleton._instance                                      
Run Code Online (Sandbox Code Playgroud)

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

但我无法找到任何支持上面'似乎'.

Mar*_*ers 5

通常,如果您调用操作的对象没有实现__getattr__,__delattr__或者__setattr__在python中实现钩子,那么是,hasattrgetattr,delattr并且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        
Run Code Online (Sandbox Code Playgroud)

故事并没有就此结束; hasattr使用getattr()(测试异常),然后可以调用Python __getattr__钩子.类似地,STORE_ATTR操作码最终可能会调用python __setattr__钩子实现.在这两种情况下,GIL都会再次释放.

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

当然,您仍然可以处理在对象协议操作期间释放锁的自定义C库.这是一件不寻常的事情.