El bucle asyncio de Python ya está en ejecución cuando se utiliza asyncio.run y se intenta añadir tareas.
Soy nuevo en asyncio y encuentro el ejemplo de EchoServer muy confuso. Estoy tratando de lograr una situación simple en la que un servidor acepte múltiples clientes, lo ejecute en una coroutine y maneje los datos, y un hilo de interfaz de usuario que maneje la entrada de ncurses. Actualmente tengo lo siguiente, que, en código, transmite la idea que pienso. Pero no funciona.
import asyncio
async def do_ui():
await asyncio.sleep(1)
print('doing')
await do_ui()
async def run_game():
loop = asyncio.get_running_loop()
server = await GameServer.create_server()
loop.create_task(server.serve_forever())
loop.create_task(do_ui())
loop.run_forever()
def run():
asyncio.run(run_game())
El problema comienza en GameServer.create_server, donde, por razones de encapsulamiento, quiero delegar la creación del servidor. Sin embargo, esta es una acción asíncrona (por alguna razón) y, por lo tanto, debe esperarse. Vea el código del servidor a continuación:
class GameServer:
@staticmethod
async def create_server():
loop = asyncio.get_running_loop()
return await loop.create_server(
lambda: GameServerProtocol(),
'127.0.0.1', 8888
)
Esto me obliga a hacer que run_game también sea asíncrono y esperarlo en el método run, que es el punto de entrada de mi setup.py, por lo que no puedo hacer eso. Sin embargo, usar el método asyncio.run inicia un bucle de eventos diferente y ya no puedo acceder a él.
¿Cómo resuelvo esto? Y para desahogarme, ¿cómo es esto de alguna manera más fácil que simplemente usar hilos?
davy.ai
Solución para lograr una situación simple en la que un servidor acepta múltiples clientes y lo ejecuta en una corrutina
Para lograr un servidor que acepte múltiples clientes y lo ejecute en una corrutina, deberá modificar la clase
GameServer
y la funciónrun_game
de la siguiente manera:La clase
GameServerProtocol
es una implementación que maneja las conexiones de clientes y la recepción de datos.La clase
GameServer
representa la instancia del servidor e incluye los métodosstart_server
ystop_server
que inician y detienen el servidor de juegos.La función
handle_clients
es responsable de iniciar el servidor y la funcióndo_ui
es responsable de manejar la entrada de la interfaz de usuario, así como de actualizar la interfaz de usuario.Finalmente, la función
run_game
crea una instancia deGameServer
, crea corrutinas para manejar el servidor y la interfaz de usuario, y luego ejecuta el bucle de eventos utilizandoasyncio.gather
.Puede llamar a
run_game
directamente para ejecutar el servidor.Por qué usar asyncio en lugar de hilos?
Hay muchas ventajas de usar asyncio en lugar de hilos:
Concurrencia sencilla: Con los hilos, la coordinación entre hilos y la ejecución paralela requiere mucha sincronización cuidadosa. En cambio, con asyncio, la naturaleza cooperativa de las corrutinas facilita la coordinación entre múltiples llamadas asíncronas.
Simplifica el flujo de control: La sintaxis de async/await que acompaña a asyncio es sencilla y fácil de entender, lo que hace que el flujo de control sea más sencillo que en un enfoque tradicional basado en devoluciones de llamada. Esto es especialmente evidente cuando se trabaja con devoluciones de llamada anidadas.
Compatibilidad multiplataforma: asyncio está integrado en el lenguaje Python, lo que lo hace inherentemente multiplataforma. Python ya es compatible con una amplia gama de sistemas operativos, lo que facilita el uso de asyncio en diferentes plataformas.
En general, asyncio proporciona un modelo más sencillo para la concurrencia que los hilos, al tiempo que es más eficiente en el uso de los recursos de la máquina.