es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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?

Tags:  , ,

Answer

  1. Avatar for 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ón run_game de la siguiente manera:

    import asyncio
    import curses
    
    class GameServerProtocol(asyncio.Protocol):
        def connection_made(self, transport):
            self.transport = transport
            self.addr = transport.get_extra_info('peername')
            print(f"Conexión desde {self.addr}")
    
        def connection_lost(self, exc):
            print(f"Conexión desde {self.addr} perdida")
    
        def data_received(self, data):
            print(f"Recibido {data} desde {self.addr}")
            self.transport.write(data)
    
    class GameServer:
        def __init__(self):
            self.server = None
    
        async def start_server(self):
            loop = asyncio.get_running_loop()
            self.server = await loop.create_server(
                lambda: GameServerProtocol(),
                '127.0.0.1', 8888)
            print('Servidor iniciado')
    
        async def stop_server(self):
            if self.server:
                self.server.close()
                await self.server.wait_closed()
                print('Servidor detenido')
    
    async def handle_clients(server):
        await server.start_server()
    
    async def do_ui(stdscr):
        stdscr.clear()
        curses.curs_set(0)
        stdscr.addstr(0, 0, "Presione 'q' para salir")
        stdscr.refresh()
    
        while True:
            key = stdscr.getch()
            if key == ord('q'):
                break
            await asyncio.sleep(0)
    
    def run_game():
        server = GameServer()
    
        loop = asyncio.get_running_loop()
        async_server = loop.create_task(handle_clients(server))
        ui = loop.run_in_executor(None, curses.wrapper, do_ui)
    
        try:
            loop.run_until_complete(asyncio.gather(async_server, ui))
        except KeyboardInterrupt:
            pass
        finally:
            loop.run_until_complete(server.stop_server())
            loop.close()
    

    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étodos start_server y stop_server que inician y detienen el servidor de juegos.

    La función handle_clients es responsable de iniciar el servidor y la función do_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 de GameServer, crea corrutinas para manejar el servidor y la interfaz de usuario, y luego ejecuta el bucle de eventos utilizando asyncio.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:

    1. Operaciones de E/S eficientes: Los hilos tienen una gran sobrecarga y crear un nuevo hilo para cada operación puede ser ineficiente. La programación asíncrona con asyncio hace un uso mucho más eficiente de los recursos del sistema combinando múltiples operaciones de E/S en un solo hilo.
    2. 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.

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

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

Comments are closed.