Entender el error de subprocesos de Python

Al leer http://bugs.python.org/msg160297 , puedo ver un sencillo script escrito por Stephen White que demuestra cómo Python subyace errores con esta excepción

Exception AttributeError: AttributeError("'_DummyThread' object has no attribute '_Thread__block'",) in <module 'threading' 

Dado el código fuente de Stephen White (http://bugs.python.org/file25511/bad-thread.py),

 import os import thread import threading import time def t(): threading.currentThread() # Populate threading._active with a DummyThread time.sleep(3) thread.start_new_thread(t, ()) time.sleep(1) pid = os.fork() if pid == 0: os._exit(0) os.waitpid(pid, 0) 

¿Cómo lo reescribiríamos para que este error se resuelva?

El error se produce debido a una mala interacción entre los objetos de subprocesos ficticios creados por la API de threading cuando se llama threading.currentThread() en un subproceso externo, y la función threading._after_fork , llamada para limpiar los recursos después de una llamada a os.fork() .

Para __stop el error sin modificar la fuente de Python, threading._DummyThread mono- threading._DummyThread con una implementación sin __stop de __stop :

 import threading threading._DummyThread._Thread__stop = lambda x: 42 

La causa del error se reduce mejor en los comentarios de Richard Oudkerk y cooyeah . Lo que pasa es lo siguiente:

  1. El módulo de threading permite que se llame a threading.currentThread() desde un subproceso no creado por las llamadas a la API de threading . A continuación, devuelve una instancia de “subproceso ficticio” que admite un subconjunto muy limitado de la API de Thread , pero sigue siendo útil para identificar el subproceso actual.

  2. threading._DummyThread se implementa como una subclase de Thread . Thread instancias de Thread normalmente contienen un invocable interno ( self.__block ) que mantiene la referencia a un locking de nivel de sistema operativo asignado para la instancia. Dado que _DummyThread _DummyThread todos los métodos de Thread públicos que podrían terminar utilizando self.__block , el constructor de _DummyThread libera intencionalmente el locking a nivel del sistema operativo eliminando self.__block .

  3. threading._after_fork rompe la encapsulación y llama al método privado Thread.__stop en todos los subprocesos registrados, incluidos los ficticios, donde nunca se tuvo la intención de invocar __stop . (No fueron iniciados por Python, de modo que Python tampoco maneja su detención). Como los hilos ficticios no conocen __stop , lo heredan de Thread , y esa implementación accede al atributo privado __block que no lo hace. existen en _DummyThread instancias _DummyThread . Este acceso finalmente provoca el error.

El error se corrige en la twig 2.7 modificando Thread.__stop para no interrumpirse cuando se elimina __block . La twig 3.x, donde __stop se deletrea como _stop y, por lo tanto, está protegida, lo corrige anulando _DummyThread ‘s _stop para no hacer nada .