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
asyncio
Utilice corrutinas para implementar bucles de eventos para lograr el efecto de tareas asincrónicas.
pip install asyncio
Las corrutinas utilizan @asyncio.coroutine
decoradores para marcar tareas asincrónicas y esperar yield from
a 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 windows
lo siguiente , se pueden lograr muchos efectos de ventana únicos y geniales. Para obtener más detalles, puede ejecutar wxDemo
el ejemplo como referencia.
# 运行 python目录下/Scripts/wxdemo.exe
C:\ProgramData\Anaconda3\envs\ctrip_code36\Scripts>wxdemo.exe
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, await
espere a que regrese la operación. Se utiliza aquí asyncio.sleep()
para simular IO
operaciones 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)
3. Crear hilo
Obtenga asyncio
el 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 task1
subtareas simultáneas :task2
task3
Master task
Primero 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
task2
y la subtarea de 4 segundostask3
y se configuró la función de devolución de llamada;Luego,
task3
el tiempo de la tarea es corto, se completa primero y se activa la devolución de llamada de la tarea;Luego
task2
se completa y se ejecuta la devolución de llamada correspondiente;Finalmente
Master task
salir.
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
Para resumir el proceso:
Obtener
asyncio
el bucle de eventosasync_loop = asyncio.get_event_loop()
;Inicie un hilo e
async_loop
inyéctelo en el hilo secundario;Configure un bucle de eventos en el subproceso y aísle el bucle de eventos de la interfaz principal
asyncio.set_event_loop(async_loop);
Úselo para
asyncio.ensure_future
configurar tareas asincrónicas;Utilice
add_done_callback
la función de devolución de llamada establecida;Se utiliza para
async_loop.run_until_complete
ejecutar 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.
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_loop
se 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()
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
El 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.