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.

Bucles asyncio: cómo implementar asyncio en un programa existente de Python – y compartir variables/datos?

Mi aplicación necesita control remoto a través de SSH. Deseo utilizar este ejemplo: https://asyncssh.readthedocs.io/en/latest/#simple-server-with-input

La aplicación original es bastante grande, utiliza GPIO y tiene 600 líneas de código y 10 bibliotecas. Por lo tanto, he creado un ejemplo simple aquí:

import asyncio, asyncssh, sys, time

aquí habría 10 bibliotecas en la aplicación original de 600 líneas

is_open = True
return value = 0;

async def handle_client(process):
    process.stdout.write('Ingrese números uno por línea, o EOF cuando termine:\n')
    process.stdout.write(is_open)

total = 0

try:
    async for line in process.stdin:
        line = line.rstrip('\n')
        if line:
            try:
                total += int(line)
            except ValueError:
                process.stderr.write('Número inválido: %s\n' % line)
except asyncssh.BreakReceived:
    pass

process.stdout.write('Total = %s\n' % total)
process.exit(0)

async def start_server():
    await asyncssh.listen('', 8022, server_host_keys=['key'],
                          authorized_client_keys='key.pub',
                          process_factory=handle_client)

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(start_server())
except (OSError, asyncssh.Error) as exc:
    sys.exit('Error al iniciar el servidor: ' + str(exc))

loop.run_forever()

aquí estaba el "antiguo" programa que ya no se ejecutaría ya que loop.run_forever() se ejecuta.

mientras True:

print(return_value)

time.sleep(0.1)

La aplicación principal se maneja principalmente con un bucle while True con muchas funciones y sleep. He comentado esa parte en el ejemplo simple anterior.

Mi pregunta es: ¿cómo debo implementar la parte SSH, que utiliza loop.run_forever() y aún así poder ejecutar mi bucle principal? Además, el handle_client(process) debe ser capaz de interactuar con variables en el programa principal (leer / escribir).

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para implementar la parte SSH manteniendo la ejecución del bucle principal, puede usar asyncio.run_coroutine_threadsafe() para ejecutar el servidor SSH en un hilo separado. Esto permitirá que el bucle principal continúe ejecutándose mientras maneja las conexiones SSH en segundo plano.

    Para interactuar con las variables en el programa principal, puede usar la clase asyncio.Queue. Cree una cola de mensajes compartida entre el programa principal y la coroutine handle_client() y use los métodos put() y get() para intercambiar datos. Esto permitirá que la coroutine handle_client() lea y modifique las variables en el programa principal.

    Aquí hay un ejemplo de cómo puede implementarlo:

    import asyncio
    import asyncssh
    import threading
    
    # crear cola de mensajes compartida
    message_queue = asyncio.Queue()
    
    # función para ejecutar el bucle principal
    def main_loop():
        while True:
            # hacer algo en el bucle principal
            mensaje = message_queue.get()
            # actualizar variables basadas en mensajes recibidos
            if mensaje == 'cerrar':
                esta_abierto = False
            elif mensaje == 'abrir':
                esta_abierto = True
    
    # programa principal
    esta_abierto = True
    valor_retorno = 0
    
    # iniciar el bucle principal en un hilo separado
    hilo = threading.Thread(target=main_loop)
    hilo.start()
    
    # coroutine para manejar las conexiones SSH
    async def handle_client(proceso):
        global esta_abierto, valor_retorno
        proceso.stdout.write('Ingrese números uno por línea o EOF cuando haya terminado:\n')
        proceso.stdout.write(str(esta_abierto))
    
        total = 0
    
        try:
            async for linea in proceso.stdin:
                linea = linea.rstrip('\n')
                if linea:
                    try:
                        total += int(linea)
                    except ValueError:
                        proceso.stderr.write('Número inválido: %s\n' % linea)
    
                # enviar mensajes al programa principal a través de la cola de mensajes
                message_queue.put(linea)
    
        except asyncssh.BreakReceived:
            pass
    
        proceso.stdout.write('Total = %s\n' % total)
        proceso.exit(0)
    
    async def start_server():
        await asyncssh.listen('', 8022, server_host_keys=['clave'],
                              authorized_client_keys='clave.pub',
                              process_factory=handle_client)
    
    # iniciar el servidor SSH en un hilo separado
    bucle = asyncio.new_event_loop()
    asyncio.set_event_loop(bucle)
    hilo = threading.Thread(target=bucle.run_until_complete, args=(start_server(),))
    hilo.start()
    
    # ejecutar el bucle principal
    while True:
        print(valor_retorno)
        time.sleep(0.1)
    

    En este ejemplo, la función main_loop() se ejecuta en un hilo separado y espera mensajes de la cola de mensajes. La coroutine handle_client() puede enviar mensajes a la cola de mensajes utilizando put(), y la función main_loop() puede leerlos utilizando get().

    Tenga en cuenta que deberá importar los módulos threading y time al principio de su código para que esto funcione.

Comments are closed.