eyed3 - ¿Cómo funciona el reactor retorcido de Python?
id3v2 4 python (2)
En caso de que no sea obvio, se llama reactor porque reacciona a las cosas . El bucle es como reacciona.
Una línea a la vez:
while True:
En realidad no es while True
; Es más como while not loop.stopped
. Puede llamar a reactor.stop()
para detener el ciclo y, de hecho, el bucle se cerrará. Pero se muestra en el ejemplo como while True
porque cuando estás escribiendo un programa de larga duración (como lo haces a menudo con Twisted) es mejor asumir que tu programa se bloqueará o se ejecutará para siempre, y que "salir de forma limpia" es No es realmente una opción.
timeout = time_until_next_timed_event()
Si expandiéramos un poco este cálculo, podría tener más sentido:
def time_until_next_timed_event():
now = time.time()
timed_events.sort(key=lambda event: event.desired_time)
soonest_event = timed_events[0]
return soonest_event.desired_time - now
timed_events
es la lista de eventos programados con reactor.callLater
; es decir, las funciones que la aplicación ha solicitado para que Twisted se ejecute en un momento determinado.
events = wait_for_events(timeout)
Esta línea aquí es la parte "mágica" de Twisted. No puedo expandir wait_for_events
de una manera general, porque su implementación depende de cómo exactamente el sistema operativo hace que los eventos deseados estén disponibles. Y, dado que los sistemas operativos son bestias complejas y complicadas, no puedo expandirlo de una manera específica a la vez que lo mantengo lo suficientemente simple como para responder a su pregunta.
Lo que pretende significar con esta función es pedir al sistema operativo, o un envoltorio de Python que lo rodea, que bloquee, hasta que uno o más de los objetos registrados previamente con él, como mínimo escuche puertos y conexiones establecidas, pero también posiblemente cosas como botones en los que se puede hacer clic - está "listo para trabajar". El trabajo puede estar leyendo algunos bytes de un socket cuando llegan de la red. El trabajo podría estar escribiendo bytes en la red cuando un búfer se vacía lo suficiente para hacerlo. Podría estar aceptando una nueva conexión o desechando una cerrada. Cada uno de estos posibles eventos son funciones que el reactor puede invocar en sus objetos: dataReceived
, buildProtocol
, resumeProducing
, etc., sobre los que aprenderá si realiza el tutorial completo de Twisted.
Una vez que tenemos nuestra lista de objetos de "eventos" hipotéticos, cada uno de los cuales tiene un método de " process
" imaginario (los nombres exactos de los métodos son diferentes en el reactor solo debido a los accidentes de la historia), luego volvemos a tratar con tiempo:
events += timed_events_until(now())
Primero, asumiendo que los events
es simplemente una list
de una clase de Event
abstracta, que tiene un método de process
que cada tipo específico de evento necesita completar.
En este punto, el bucle se ha "despertado", porque wait_for_events
, dejó de bloquear. Sin embargo, no sabemos cuántos eventos programados deberíamos ejecutar según el tiempo que estuvo "dormido" . Podríamos haber dormido durante todo el tiempo de espera si no hubiera pasado nada, pero si muchas conexiones estuvieran activas, podríamos haber dormido, efectivamente, sin tiempo. Por lo tanto, verificamos la hora actual (" now()
"), y agregamos a la lista de eventos que necesitamos procesar, todos los eventos cronometrados con una desired_time
que está en o antes de la hora actual.
Finalmente,
for event in events:
event.process()
Esto solo significa que Twisted repasa la lista de cosas que tiene que hacer y las hace. En realidad, por supuesto, maneja las excepciones alrededor de cada evento, y la implementación concreta del reactor a menudo solo llama directamente a un controlador de eventos en lugar de crear un objeto similar a un Event
para registrar el trabajo que debe hacerse primero, pero conceptualmente esto es simplemente lo que pasa. event.process
aquí podría significar llamar a socket.recv()
y luego a yourProtocol.dataReceived
con el resultado, por ejemplo.
Espero que esta explicación ampliada te ayude a entenderlo. Si desea obtener más información sobre Twisted trabajando en ello, lo invito a unirse a la lista de correo , subirse al canal de IRC, #twisted
para hablar sobre aplicaciones o #twisted-dev
para trabajar en Twisted, ambos en Freenode .
Recientemente, he estado buceando en los documentos Twisted. De lo que he recopilado, la base de la funcionalidad de Twisted es el resultado de su bucle de eventos llamado "Reactor". El reactor escucha ciertos eventos y los envía a las funciones de devolución de llamada registradas que han sido diseñadas para manejar estos eventos. En el libro, hay un pseudo código que describe lo que hace el Reactor, pero tengo problemas para entenderlo, simplemente no tiene ningún sentido para mí.
while True:
timeout = time_until_next_timed_event()
events = wait_for_events(timeout)
events += timed_events_until(now())
for event in events:
event.process()
¿Qué significa esto?
Intentaré elaborar:
El programa cede el control y se pone a dormir a la espera de los eventos. Supongo que la parte más interesante aquí es el evento. El evento es: a solicitud externa (recibir el paquete de red, hacer clic en un teclado, temporizador, llamada de programa diferente) el programa recibe el control (en algún otro hilo o en una rutina especial). De alguna manera, el tiempo de espera en wait_for_events se interrumpe y se devuelve wait_for_events .
En esa ocurrencia de control, el controlador de eventos almacena la información de ese evento en alguna estructura de datos, eventos , que luego se usa para hacer algo acerca de esos eventos ( evento-> proceso ). Pueden ocurrir no solo uno, sino muchos eventos en el tiempo entre la entrada y salida de wait_for_events , todos ellos deben procesarse. El procedimiento event-> process () es personalizado y generalmente debería llamar a la parte interesante: el código retorcido del usuario.