¿Es seguro el hilo getattr / setattr / hasattr / delattr?

Ver esta implementación Singleton :

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

Parece que “Singleton._instance = ..” (algo como setattr) y hasattr son atómicos. O hasattr no conducirá a un locking debido a setattr.

Pero no puedo encontrar ninguno para apoyar “parece”.

Por lo general , siempre que el objeto en el que __getattr__ las operaciones no implemente los __getattr__ , __delattr__ o __setattr__ en python, entonces sí, hasattr , getattr , delattr y setattr son operaciones atómicas.

Cualquier código de bytes individual es una operación atómica en lo que concierne a los hilos de Python. El bucle de evaluación de Python toma el locking de intérprete global (GIL) al interpretar los códigos de operación.

Necesitaría mirar el código de bytes para ver dónde se encuentran los límites:

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

La historia no termina ahí; hasattr usa getattr() (prueba una excepción), que a su vez puede invocar el enganche __getattr__ Python. De manera similar, el STORE_ATTR operación STORE_ATTR podría terminar llamando a una implementación de enganche __setattr__ python. En ambos casos el GIL sería liberado de nuevo.

Para las implementaciones predeterminadas ( Singleton no implementa esos enganches), las operaciones son atómicas, ya que el código C de Python maneja toda la operación sin recurrir a Python y, por lo tanto, al bucle de evaluación (donde GIL podría liberarse y bloquearse nuevamente para otro hilo).

Por supuesto, aún podría estar tratando con una biblioteca C personalizada que libera el locking durante las operaciones de protocolo de objeto . Eso sería una cosa inusual para hacer.