getattr/setattr/hasattr/delattr 线程安全吗? [英] Is getattr/setattr/hasattr/delattr thread safe?
问题描述
看这个单例实现:
如果不是 hasattr(Singleton, "_instance"):使用 Singleton._instance_lock:如果不是 hasattr(Singleton, "_instance"):Singleton._instance = Singleton()返回 Singleton._instance
似乎Singleton._instance = .."(类似于setattr)和hasattr 是原子的.或者 hasattr 不会因为 setattr 导致崩溃.
但我在似乎"之上找不到任何支持.
通常,前提是您调用操作的对象没有实现 __getattr__
, __delattr__
或 __setattr__
钩子在 python 中,然后是的,hasattr
,getattr
,delattr
和 setattr
是原子操作.
就 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屋!