Utilice asyncio para implementar operaciones de interfaz multitarea asincrónicas de subprocesos múltiples

b4e0f5fffa7dcf7e67f7c6ccfcaed65b.gif

Prefacio

Recientemente, hay un proyecto que requiere solicitudes multitarea asincrónicas iniciadas desde el cliente de ventana. Para evitar que las operaciones en segundo plano a largo plazo bloqueen las operaciones en primer plano, es necesario iniciar subprocesos múltiples para aislar el bucle de eventos de la interfaz principal de la interfaz de usuario y el bucle de eventos de tareas asincrónicas en segundo plano asyncio.

Introducción a las bibliotecas de soporte

1. Biblioteca de rutinas asincrónicas asyncio

asyncioUtilice corrutinas para implementar bucles de eventos para lograr el efecto de tareas asincrónicas.

pip install asyncio

Las corrutinas utilizan @asyncio.coroutinedecoradores para marcar tareas asincrónicas y esperar yield froma que se completen. en

  • @asyncio.coroutine === asíncrono

  • rendimiento de === esperar

Desde que se introdujo la palabra clave async/await, la implementación del código se ha vuelto mucho más elegante.

2. Biblioteca de interfaz wxPython

wxPython es un conjunto de herramientas GUI multiplataforma para el lenguaje de programación Python. Permite a los programadores de Python crear programas de forma sencilla y sencilla con interfaces gráficas de usuario potentes y potentes. Se implementa como un conjunto de módulos de extensión de Python que envuelven los componentes GUI de la popular biblioteca multiplataforma wxWidgets, que está escrita en C++.

pip install wxpython

Especialmente en windowslo siguiente , se pueden lograr muchos efectos de ventana únicos y geniales. Para obtener más detalles, puede ejecutar wxDemoel ejemplo como referencia.

# 运行 python目录下/Scripts/wxdemo.exe
C:\ProgramData\Anaconda3\envs\ctrip_code36\Scripts>wxdemo.exe
162dd50a9996c146ca9b33133844684a.png

Corrutina multitarea

1. Crea una tarea asincrónica

Primero, simplemente creamos una tarea asincrónica, aleatorizamos la duración de la tarea y registramos la hora de inicio y finalización de la tarea.

async def AsyncTask(self, name):
    t = random.randint(1, 5)
    self.log('Start %s: %.8f, duration %ds' % (name, time.time() - self.start, t))
    await asyncio.sleep(t)  # 模拟长时间后台操作
    self.log('End %s: %.8f' % (name, time.time() - self.start))
    return name

async palabra clave para definir una función asincrónica, awaitespere a que regrese la operación. Se utiliza aquí asyncio.sleep()para simular IOoperaciones reales.

Configure una función de devolución de llamada para que se active después de que finalice la tarea asincrónica.

def task_callback(self, task):
    time.sleep(1)
    self.log('Callback %s: %.8f' % (task.result(), time.time() - self.start))

2. Construir interfaz de ventana

Dibuje una ventana simple que contenga un botón para activar una tarea asincrónica y un texto estático para mostrar el estado actual de la tarea.

class MyFrame(wx.Frame):
    def __init__(
            self, parent, ID, title, pos=wx.DefaultPosition,
            size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE
    ):
        wx.Frame.__init__(self, parent, ID, title, pos, size, style)
        panel = wx.Panel(self, -1)

        self.start = time.time()  # 记录初始时间戳

        btn = wx.Button(panel, -1, "启动异步任务", size=(100, 30))
        btn.SetPosition((20, 20))

        txt = wx.StaticText(panel, -1, "任务未运行", (200, 40))
        txt.SetPosition((30, 70))
        self.txt = txt

        self.Bind(wx.EVT_BUTTON, self.OnButton, btn)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
3e23eee0462cf73c2bf2f551407c0a3b.png

3. Crear hilo

Obtenga asyncioel bucle de eventos, inicie un hilo independiente y aísle los eventos de la interfaz principal

def OnButton(self, evt):
    async_loop = asyncio.get_event_loop()
    if not async_loop.is_running():
        threading.Thread(target=self._asyncio_thread, args=(async_loop,)).start()
    return

4. Crea la tarea principal.

Configure una tarea principal, que contenga una subtarea independiente y un conjunto de task1subtareas simultáneas :task2task3

  • Master taskPrimero comienza la tarea principal ;

  • Se inicia una subtarea de 5 segundos task1;

  • Una vez completada, se iniciaron al mismo tiempo la subtarea de 5 segundos task2y la subtarea de 4 segundos task3y se configuró la función de devolución de llamada;

  • Luego, task3el tiempo de la tarea es corto, se completa primero y se activa la devolución de llamada de la tarea;

  • Luego task2se completa y se ejecuta la devolución de llamada correspondiente;

  • Finalmente Master tasksalir.

def _asyncio_thread(self, async_loop):
    self.log('Start %s: %.8f' % ('Master task', time.time() - self.start))
    asyncio.set_event_loop(async_loop)

    print('异步单任务', 'task1')
    task = asyncio.ensure_future(self.AsyncTask('task1'))
    async_loop.run_until_complete(task)

    print('异步多任务并发', 'task2', 'task3')
    tasks = []
    task = asyncio.ensure_future(self.AsyncTask('task2'))
    task.add_done_callback(self.task_callback)
    tasks.append(task)  # 将多个任务对象装在到一个任务列表中

    task = asyncio.ensure_future(self.AsyncTask('task3'))
    task.add_done_callback(self.task_callback)
    tasks.append(task)

    async_loop.run_until_complete(asyncio.wait(tasks))

    self.log('End %s: %.8f' % ('Master task', time.time() - self.start))
    return
694798e38abf429d5ff5dbb765816de4.png

Para resumir el proceso:

  • Obtener asyncioel bucle de eventos async_loop = asyncio.get_event_loop();

  • Inicie un hilo e async_loopinyéctelo en el hilo secundario;

  • Configure un bucle de eventos en el subproceso y aísle el bucle de eventos de la interfaz principalasyncio.set_event_loop(async_loop);

  • Úselo para asyncio.ensure_futureconfigurar tareas asincrónicas;

  • Utilice add_done_callbackla función de devolución de llamada establecida;

  • Se utiliza para async_loop.run_until_completeejecutar funciones asincrónicas.

Corrutinas multitarea concurrentes

Si no hay dependencias entre subtareas, se puede utilizar la ejecución concurrente para mejorar en gran medida la eficiencia de ejecución del programa.

1d3575cf30a2a99b2ec1b2f85220de7c.png

1. Cree múltiples tareas asincrónicas

def OnButton(self, evt):
    async_loop = asyncio.get_event_loop()
    if not async_loop.is_running():
        t = threading.Thread(target=self._asyncio_thread, args=(async_loop,))
        t.start()

    for i in range(5):
        asyncio.run_coroutine_threadsafe(self.AsyncTask('task%d' % i), async_loop)

2. Crear hilo

def _asyncio_thread(self, async_loop):
    asyncio.set_event_loop(async_loop)
    async_loop.run_forever()

3. Salga del bucle de eventos

Vale la pena señalar que dado que async_loopse run_forever()inicia el hilo en el hilo, al cerrar el programa, call_soon_threadsafe(async_loop.stop)se deben realizar las operaciones de limpieza correspondientes para garantizar que el programa salga sin problemas.

def OnCloseWindow(self, evt):
    async_loop = asyncio.get_event_loop()
    async_loop.call_soon_threadsafe(async_loop.stop)
    self.Destroy()
539d2ff42c6fbe9a1555ec34843bfbc1.png

Otras lecturas

Para obtener más información, puede consultar "Async IO en Python: un tutorial completo", que es muy detallado desde los principios básicos hasta los patrones de diseño.

https://realpython.com/async-io-python/

Descarga del código fuente

ff7f529709a6c88f89d6b33937c78ef4.pngEl código fuente completo de este problema se puede encontrar en la cuenta pública "Deep Awakening" y responder: "asyncio" en segundo plano para obtener el enlace de descarga.

PD: cuando codificamos a altas horas de la noche, un codificador es esencialmente una criatura que convierte el café en código. Sin embargo, mi cuerpo no puede soportar beber demasiado café, así que recientemente dejé el café y me obsesioné con el té de pomelo.

Recomiendo el té de pomelo y miel de Hengshoutang. Compre uno y llévese otro gratis. Sabe mejor que el coreano. Por cierto, apoyo los productos nacionales.

7f464902a2b5b5d5406f0ed2dcf2fb8c.gif

Supongo que te gusta

Origin blog.csdn.net/weixin_47479625/article/details/120793384
Recomendado
Clasificación