通常、操作を呼び出すオブジェクトがPython で__getattr__
、__delattr__
または__setattr__
フックを実装していない場合、はい、、hasattr
、getattr
はアトミック操作です。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
話はそれだけではありません。hasattr
(例外のテスト) を使用しgetattr()
、Python__getattr__
フックを呼び出すことができます。同様に、オペコードは最終的に PythonフックSTORE_ATTR
の実装を呼び出す可能性があります。__setattr__
どちらの場合も、GIL は再び解放されます。
デフォルトの実装 (Singleton
はこれらのフックを実装していません) では、Python C コードが Python にフォールバックすることなく操作全体を処理するため、操作はアトミックです。したがって、評価ループ (GIL が解放され、別のスレッドのために再度ロックされる可能性があります)。
もちろん、オブジェクト プロトコルの操作中にロックを解除するカスタム C ライブラリを扱うこともできます。それは異常なことだろう。