threads temporizador nodejs node nexttick loop event child c++ python blocking event-loop

c++ - temporizador - ¿Cómo implementarías un ciclo de eventos básico?



nodejs clock (4)

Si ha trabajado con gui toolkits, sabe que hay un bucle de eventos / bucle principal que debe ejecutarse después de que se haga todo, y que mantendrá la aplicación activa y en respuesta a diferentes eventos. Por ejemplo, para Qt, haría esto en main ():

int main() { QApplication app(argc, argv); // init code return app.exec(); }

Que en este caso, app.exec () es el bucle principal de la aplicación.

La forma obvia de implementar este tipo de bucle sería:

void exec() { while (1) { process_events(); // create a thread for each new event (possibly?) } }

Pero esto limita la CPU al 100% y es prácticamente inútil. Ahora, ¿cómo puedo implementar un bucle de eventos de este tipo que responda sin consumir la CPU por completo?

Las respuestas son apreciadas en Python y / o C ++. Gracias.

Nota al pie: Para aprender, implementaré mis propias señales / ranuras, y las usaría para generar eventos personalizados (por ejemplo, go_forward_event(steps) ). Pero si sabe cómo puedo usar los eventos del sistema manualmente, también me gustaría saberlo.


Generalmente haría esto con algún tipo de semáforo de conteo :

  1. El semáforo comienza en cero.
  2. El bucle de eventos espera en el semáforo.
  3. El evento (s) entra, el semáforo se incrementa.
  4. El controlador de eventos desbloquea y disminuye el semáforo y procesa el evento.
  5. Cuando se procesan todos los eventos, el semáforo es cero y los bucles de evento se bloquean nuevamente.

Si no quieres complicarte tanto, puedes agregar una llamada sleep () en tu bucle while con un tiempo de inactividad trivialmente pequeño. Eso hará que el subproceso de procesamiento de mensajes ceda el tiempo de CPU a otros subprocesos. La CPU no se fijará al 100% más, pero sigue siendo un desperdicio.


Pitón:

Puede ver la implementación del reactor retorcido, que es probablemente la mejor implementación para un bucle de eventos en Python. Los reactores en Twisted son implementaciones de una interfaz y puede especificar un tipo de reactor para ejecutar: select, epoll, kqueue (todos basados ​​en ac api que usan esas llamadas al sistema), también hay reactores basados ​​en los kits de herramientas QT y GTK.

Una implementación simple sería utilizar select:

#echo server that accepts multiple client connections without forking threads import select import socket import sys host = '''' port = 50000 backlog = 5 size = 1024 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((host,port)) server.listen(backlog) input = [server,sys.stdin] running = 1 #the eventloop running while running: inputready,outputready,exceptready = select.select(input,[],[]) for s in inputready: if s == server: # handle the server socket client, address = server.accept() input.append(client) elif s == sys.stdin: # handle standard input junk = sys.stdin.readline() running = 0 else: # handle all other sockets data = s.recv(size) if data: s.send(data) else: s.close() input.remove(s) server.close()


Solía ​​preguntarme mucho sobre lo mismo!

Un bucle principal de GUI tiene este aspecto, en pseudo-código:

void App::exec() { for(;;) { vector<Waitable> waitables; waitables.push_back(m_networkSocket); waitables.push_back(m_xConnection); waitables.push_back(m_globalTimer); Waitable* whatHappened = System::waitOnAll(waitables); switch(whatHappened) { case &m_networkSocket: readAndDispatchNetworkEvent(); break; case &m_xConnection: readAndDispatchGuiEvent(); break; case &m_globalTimer: readAndDispatchTimerEvent(); break; } } }

¿Qué es un "Waitable"? Bueno, es dependiente del sistema. En UNIX se llama "descriptor de archivo" y "waitOnAll" es la llamada al sistema de selección. El llamado vector<Waitable> es un ::fd_set en UNIX, y "whatHappened" se consulta en realidad a través de FD_ISSET . Los controles waitable reales se adquieren de varias formas, por ejemplo, m_xConnection puede tomarse de :: XConnectionNumber (). X11 también proporciona una API portátil de alto nivel para esto - :: XNextEvent () - pero si lo utilizara, no podría esperar en varios orígenes de eventos simultáneamente .

¿Cómo funciona el bloqueo? "waitOnAll" es un syscall que le dice al sistema operativo que ponga su proceso en una "lista de espera". Esto significa que no se le da tiempo de CPU hasta que ocurra un evento en uno de los waitables. Esto, entonces, significa que su proceso está inactivo, consumiendo 0% de CPU. Cuando ocurre un evento, su proceso reaccionará brevemente y luego volverá al estado inactivo. Las aplicaciones GUI pasan casi todo el tiempo inactivas.

¿Qué pasa con todos los ciclos de CPU mientras estás durmiendo? Depende. A veces, otro proceso tendrá un uso para ellos. Si no es así, su sistema operativo hará un ciclo de ocupado de la CPU o lo pondrá en modo de bajo consumo temporal, etc.

¡Por favor pregunte por mas detalles!


Usaría una biblioteca de mensajes simple y ligera llamada ZeroMQ ( http://www.zeromq.org/ ). Es una biblioteca de código abierto (LGPL). Esta es una biblioteca muy pequeña; En mi servidor, todo el proyecto se compila en unos 60 segundos.

ZeroMQ simplificará enormemente su código impulsado por eventos, Y también es LA solución más eficiente en términos de rendimiento. La comunicación entre hilos que utilizan ZeroMQ es mucho más rápida (en términos de velocidad) que el uso de semáforos o sockets locales de UNIX. ZeroMQ también puede ser una solución 100% portátil, mientras que todas las demás soluciones atarían su código a un sistema operativo específico.