Cuatro tareas en paralelo... ¿cómo hago eso?

Cuatro tareas en paralelo... ¿cómo hago eso?

Tengo un montón de imágenes PNG en un directorio. Tengo una aplicación llamada pngout que ejecuto para comprimir estas imágenes. Esta aplicación se llama mediante un script que hice. El problema es que este script hace uno a la vez, algo como esto:

FILES=(./*.png)
for f in  "${FILES[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 $f R${f/\.\//}
done

Procesar un solo archivo a la vez lleva mucho tiempo. Después de ejecutar esta aplicación, veo que la CPU es solo del 10%. Entonces descubrí que puedo dividir estos archivos en 4 lotes, colocar cada lote en un directorio y activar 4, desde cuatro ventanas de terminal, cuatro procesos, de modo que tengo cuatro instancias de mi script, al mismo tiempo, procesando esas imágenes y el El trabajo toma 1/4 del tiempo.

El segundo problema es que perdí tiempo dividiendo las imágenes y lotes y copiando el script en cuatro directorios, abrí 4 ventanas de terminal, bla bla...

¿Cómo hacer eso con un guión, sin tener que dividir nada?

Me refiero a dos cosas: primero, ¿cómo puedo, desde un script bash, iniciar un proceso en segundo plano? (¿simplemente agregue & al final?) Segundo: ¿cómo dejo de enviar tareas a segundo plano después de enviar la cuarta tarea y hago que el script espere hasta que finalicen las tareas? Quiero decir, ¿simplemente enviar una nueva tarea a un segundo plano cuando finaliza una de ellas, manteniendo siempre 4 tareas en paralelo? si no lo hago, el bucle disparará millones de tareas en segundo plano y la CPU se obstruirá.

Respuesta1

Si tiene una copia xargsque admita la ejecución paralela -P, simplemente puede hacer

printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}

Para otras ideas, la wiki de Woolledge Bash tiene unasecciónen el artículo Gestión de procesos que describe exactamente lo que desea.

Respuesta2

Además de las soluciones ya propuestas, puede crear un archivo MAKE que describa cómo crear un archivo comprimido a partir de un archivo sin comprimir y utilizarlo make -j 4para ejecutar 4 trabajos en paralelo. El problema es que necesitará nombrar los archivos comprimidos y sin comprimir de manera diferente, o almacenarlos en directorios diferentes; de lo contrario, será imposible escribir una regla de creación razonable.

Respuesta3

Si tienes GNU Paralelohttp://www.gnu.org/software/parallel/instalado puedes hacer esto:

parallel ./pngout -s0 {} R{} ::: *.png

Puede instalar GNU Parallel simplemente mediante:

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

Mire los vídeos de introducción de GNU Parallel para obtener más información: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Respuesta4

Para responder a tus dos preguntas:

  • sí, agregar & al final de la línea le indicará al shell que inicie un proceso en segundo plano.
  • Con el waitcomando, puede pedirle al shell que espere a que finalicen todos los procesos en segundo plano antes de continuar.

Aquí está el script modificado para que jse utilice para realizar un seguimiento del número de procesos en segundo plano. Cuando NB_CONCURRENT_PROCESSESse alcance, el script se restablecerá ja 0 y esperará a que finalicen todos los procesos en segundo plano antes de reanudar su ejecución.

files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
        echo "Processing $f file..."
        # take action on each file. $f store current file name
        ./pngout -s0 "$f" R"${f/\.\//}" &
        ((++j == nb_concurrent_processes)) && { j=0; wait; }
done

información relacionada