Quatro tarefas em paralelo... como faço isso?

Quatro tarefas em paralelo... como faço isso?

Eu tenho um monte de imagens PNG em um diretório. Eu tenho um aplicativo chamado pngout que executo para compactar essas imagens. Este aplicativo é chamado por um script que fiz. O problema é que esse script faz uma de cada vez, algo assim:

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

Processar apenas um arquivo por vez leva muito tempo. Depois de executar este aplicativo, vejo que a CPU está apenas 10%. Então descobri que posso dividir esses arquivos em 4 lotes, colocar cada lote em um diretório e disparar 4, de quatro janelas de terminal, quatro processos, então tenho quatro instâncias do meu script, ao mesmo tempo, processando essas imagens e o o trabalho leva 1/4 do tempo.

O segundo problema é que perdi tempo dividindo as imagens e lotes e copiando o script para quatro diretórios, abri 4 janelas de terminal, bla bla...

Como fazer isso com um script, sem precisar dividir nada?

Quero dizer duas coisas: primeiro, como faço para, a partir de um script bash, disparar um processo para segundo plano? (basta adicionar & no final?) Segundo: como faço para parar de enviar tarefas para segundo plano após enviar as quartas tarefas e colocar o script para esperar até que as tarefas terminem? Quer dizer, apenas enviar uma nova tarefa para segundo plano quando uma tarefa termina, mantendo sempre 4 tarefas em paralelo? se eu não fizer isso, o loop disparará zilhões de tarefas em segundo plano e a CPU ficará obstruída.

Responder1

Se você tiver uma cópia xargsque suporte execução paralela com -P, você pode simplesmente fazer

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

Para outras ideias, o wiki Wooledge Bash tem umseçãono artigo Gerenciamento de processos descrevendo exatamente o que você deseja.

Responder2

Além das soluções já propostas, você pode criar um makefile que descreve como fazer um arquivo compactado a partir de um descompactado, e usar make -j 4para executar 4 jobs em paralelo. O problema é que você precisará nomear arquivos compactados e descompactados de maneira diferente ou armazená-los em diretórios diferentes; caso contrário, será impossível escrever uma regra make razoável.

Responder3

Se você possui GNU Paralelohttp://www.gnu.org/software/parallel/instalado você pode fazer isso:

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

Você pode instalar o GNU Parallel simplesmente:

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

Assista aos vídeos de introdução do GNU Parallel para saber mais: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Responder4

Para responder às suas duas perguntas:

  • sim, adicionar & no final da linha instruirá seu shell a iniciar um processo em segundo plano.
  • usando o waitcomando, você pode pedir ao shell para aguardar a conclusão de todos os processos em segundo plano antes de prosseguir.

Aqui está o script modificado para jser usado para controlar o número de processos em segundo plano. Quando NB_CONCURRENT_PROCESSESfor atingido, o script será redefinido jpara 0 e aguardará a conclusão de todos os processos em segundo plano antes de retomar sua execução.

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

informação relacionada