getattr/setattr/hasattr/delattr 线程安全吗? [英] Is getattr/setattr/hasattr/delattr thread safe?

查看:48
本文介绍了getattr/setattr/hasattr/delattr 线程安全吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看这个单例实现:

如果不是 hasattr(Singleton, "_instance"):使用 Singleton._instance_lock:如果不是 hasattr(Singleton, "_instance"):Singleton._instance = Singleton()返回 Singleton._instance

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

但我在似乎"之上找不到任何支持.

解决方案

通常,前提是您调用操作的对象没有实现 __getattr__, __delattr____setattr__ 钩子在 python 中,然后是的,hasattrgetattrdelattrsetattr 是原子操作.

就 Python 线程而言,任何单个字节码都是原子操作.Python 求值循环在解释操作码时获取全局解释器锁 (GIL).

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

<预><代码>>>>定义 foo():...如果不是 hasattr(Singleton, "_instance"):...使用 Singleton._instance_lock:...如果不是 hasattr(Singleton, "_instance"):... Singleton._instance = Singleton()...返回 Singleton._instance...>>>dis.dis(foo)2 0 LOAD_GLOBAL 0 (hasattr)3 LOAD_GLOBAL 1(单例)6 LOAD_CONST 1 ('_instance')9 CALL_FUNCTION 212 POP_JUMP_IF_TRUE 643 15 LOAD_GLOBAL 1(单例)18 LOAD_ATTR 2 (_instance_lock)21 SETUP_WITH 35(到 59)24 POP_TOP4 25 LOAD_GLOBAL 0 (hasattr)28 LOAD_GLOBAL 1(单例)31 LOAD_CONST 1 ('_instance')34 CALL_FUNCTION 237 POP_JUMP_IF_TRUE 555 40 LOAD_GLOBAL 1(单例)43 CALL_FUNCTION 046 LOAD_GLOBAL 1(单例)49 STORE_ATTR 3 (_instance)52 JUMP_FORWARD 0(到 55)>>55 POP_BLOCK56 LOAD_CONST 0(无)>>第59话60 END_FINALLY61 JUMP_FORWARD 0(到 64)6 >>64 LOAD_GLOBAL 1(单例)67 LOAD_ATTR 3 (_instance)70 RETURN_VALUE

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

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

当然,您仍然可以处理在 对象协议操作.那将是一件不寻常的事情.

See this Singleton implementation:

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

It seems that "Singleton._instance = .."(something like setattr) and hasattr are atomic. Or hasattr won't lead to a crash because of setattr.

But I cant find any to support above 'seems'.

解决方案

Usually, provided the object you call the operations on does not implement the __getattr__, __delattr__ or __setattr__ hooks in python, then yes, hasattr, getattr, delattr and setattr are atomic operations.

Any individual bytecode is an atomic operation as far as Python threads are concerned. The Python evaluation loop grabs the Global Interpreter Lock (GIL) while interpreting opcodes.

You'd need to look at the bytecode to see where the boundaries lie:

>>> 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        

The story doesn't end there; hasattr uses getattr() (tests for an exception), which in turn can invoke the Python __getattr__ hook. Similarly, the STORE_ATTR opcode could end up calling a python __setattr__ hook implementation. In both cases the GIL would be released again.

For default implementations (Singleton does not implement those hooks) the operations are atomic as Python C code handles the whole operation without falling back to Python and thus the evaluation loop (where the GIL might be released and locked again for another thread).

Of course, you could still be dealing with a custom C library that releases the lock during object protocol operations. That'd be a an unusual thing to do.

这篇关于getattr/setattr/hasattr/delattr 线程安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆