Usando Tqdm con Asyncio en Python

¡Haz una fortuna con tu pequeña mano, dale un pulgar hacia arriba!

Introducción

preocupado

No es raro que los científicos de datos usen programación simultánea en Python para aumentar la productividad. Siempre es satisfactorio ver varios procesos secundarios o subprocesos simultáneos en segundo plano para mantener en orden mis tareas de cómputo o enlazadas a IO.

Pero hay otra cosa que me molesta: cuando proceso cientos o miles de archivos o ejecuto cientos o miles de procesos simultáneamente en segundo plano, siempre me preocupa que algunas tareas se cuelguen en secreto y todo el código se ejecute para siempre. finalizado. También me cuesta saber dónde se está ejecutando el código en este momento.

Lo peor de todo es que cuando miro una pantalla en blanco, es difícil saber cuánto tardará en ejecutarse mi código o cuál es el ETA. Esto es muy perjudicial para mi capacidad de organizar mi horario de trabajo.

Por lo tanto, quiero una forma de saber a dónde va la ejecución del código.

Método existente

El método más tradicional es compartir un área de memoria entre tareas, poner un contador en esta área de memoria, dejar que este contador haga +1 cuando finalice una tarea y luego usar un hilo para imprimir continuamente el valor de este contador.

Esta nunca es una buena solución: por un lado, necesito agregar un código para contar a su lógica comercial existente, lo que viola el principio de "bajo acoplamiento, alta cohesión". Por otro lado, debo tener mucho cuidado con los mecanismos de bloqueo debido a problemas de seguridad de subprocesos, que causan problemas de rendimiento innecesarios.

tqdm

alternativa

Un día, descubrí la biblioteca tqdm, que usa una barra de progreso para visualizar el progreso de mi código. ¿Puedo usar una barra de progreso para visualizar la finalización y el ETA de mis tareas asyncio?

Luego, en este artículo [1] , compartiré este método con todos, para que cada programador tenga la oportunidad de monitorear el progreso de sus tareas concurrentes.

asincrónico

Antes de comenzar, quiero que tenga algunos antecedentes sobre Python asyncio. Mi artículo describe el uso de algunas API comunes de asyncio [2] , que nos ayudarán a comprender mejor el diseño de tqdm:

alternativa

descripción general de tqdm

Como se indica en el sitio oficial, tqdm es una herramienta que muestra una barra de progreso circular. Es fácil de usar, altamente personalizable y tiene un bajo consumo de recursos.

Un uso típico es pasar un iterable al constructor tqdm y obtendrá una barra de progreso como esta:

from time import sleep
from tqdm import tqdm


def main():
    for _ in tqdm(range(100)):
        # do something in the loop
        sleep(0.1)


if __name__ == "__main__":
    main()

O puede buscar y actualizar manualmente el progreso de la barra de progreso mientras lee el archivo:

import os
from tqdm import tqdm


def main():
    filename = "../data/large-dataset"
    with (tqdm(total=os.path.getsize(filename)) as bar,
            open(filename, "r", encoding="utf-8"as f):
        for line in f:
            bar.update(len(line))


if __name__ == "__main__":
    main()
alternativa

Integrar tqdm con asíncrono

En general, tqdm es muy fácil de usar. Sin embargo, se necesita más información sobre la integración de tqdm con asyncio en GitHub. Así que busqué en el código fuente para ver si tqdm es compatible con asyncio.

Afortunadamente, las versiones recientes de tqdm proporcionan el paquete tqdm.asyncio, que proporciona la clase tqdm_asyncio.

La clase tqdm_asyncio tiene dos métodos relacionados. Uno es tqdm_asyncio.as_completed. Como se puede ver en el código fuente, es un contenedor para asyncio.as_completed:

@classmethod
    def as_completed(cls, fs, *, loop=None, timeout=None, total=None, **tqdm_kwargs):
        """
        Wrapper for `asyncio.as_completed`.
        """

        if total is None:
            total = len(fs)
        kwargs = {}
        if version_info[:2] < (310):
            kwargs['loop'] = loop
        yield from cls(asyncio.as_completed(fs, timeout=timeout, **kwargs),
                       total=total, **tqdm_kwargs)

El otro es tqdm_asyncio.gather, como se puede ver en el código fuente, se basa en la implementación de tqdm_asyncio.as_completed que simula la función de asyncio.gather:

@classmethod
    async def gather(cls, *fs, loop=None, timeout=None, total=None, **tqdm_kwargs):
        """
        Wrapper for `asyncio.gather`.
        """

        async def wrap_awaitable(i, f):
            return i, await f

        ifs = [wrap_awaitable(i, f) for i, f in enumerate(fs)]
        res = [await f for f in cls.as_completed(ifs, loop=loop, timeout=timeout,
                                                 total=total, **tqdm_kwargs)]
        return [i for _, i in sorted(res)]

Entonces, a continuación, describiré el uso de estas dos API. Antes de comenzar, necesitamos hacer un trabajo preparatorio. Aquí escribí un método simple para simular una tarea simultánea con tiempos de sueño aleatorios:

import asyncio
import random

from tqdm.asyncio import tqdm_asyncio


class AsyncException(Exception):
    def __int__(self, message):
        super.__init__(self, message)


async def some_coro(simu_exception=False):
    delay = round(random.uniform(1.05.0), 2)

    # We will simulate throwing an exception if simu_exception is True
    if delay > 4 and simu_exception:
        raise AsyncException("something wrong!")

    await asyncio.sleep(delay)

    return delay

A continuación, crearemos 2000 tareas simultáneas y luego usaremos tqdm_asyncio.gather en lugar del método familiar asyncio.gather para ver si la barra de progreso funciona:

async def main():
    tasks = []
    for _ in range(2000):
        tasks.append(some_coro())
    await tqdm_asyncio.gather(*tasks)

    print(f"All tasks done.")


if __name__ == "__main__":
    asyncio.run(main())
alternativa

O reemplacemos tqdm_asyncio.gather con tqdm_asyncio.as_completed y vuelva a intentarlo:

async def main():
    tasks = []
    for _ in range(2000):
        tasks.append(some_coro())

    for done in tqdm_asyncio.as_completed(tasks):
        await done

    print(f"The tqdm_asyncio.as_completed also works fine.")


if __name__ == "__main__":
    asyncio.run(main())
alternativa

Referencia

[1]

Fuente:https://towardsdatascience.com/using-tqdm-with-asyncio-in-python-5c0f6e747d55

[2]

asincrónico:https://github.com/Jwindler/Ice_story

Este artículo es publicado por mdnice multiplataforma

Supongo que te gusta

Origin blog.csdn.net/swindler_ice/article/details/130569351
Recomendado
Clasificación