Execução paralela de um programa em vários arquivos

7

Eu tenho um pequeno script que percorre todos os arquivos de uma pasta e executa um comando (geralmente de longa duração). Basicamente é

for file in ./folder/*;
do
    ./bin/myProgram $file > ./done/$file
done

(Por favor, ignore erros de sintaxe, é apenas um pseudo código).

Eu queria agora executar este script duas vezes ao mesmo tempo. Obviamente, a execução é desnecessária se existir o arquivo ./done/$. Então eu mudei o script para

for file in ./folder/*;
do
    [ -f ./done/$file ] || ./bin/myProgram $file >./done/$file
done

Então basicamente a questão é: É possível que ambos os scripts (ou, em geral, mais de um script), na verdade, estejam no mesmo ponto e verifique a existência do arquivo done que falha e o comando é executado duas vezes?

seria simplesmente perfeito, mas duvido muito. Isso seria muito fácil: D Se acontecer de eles processarem o mesmo arquivo, é possível "sincronizar" os scripts de alguma forma?

    
por stefan 10.05.2012 / 10:53

4 respostas

4

Isso é possível e ocorre na realidade. Use um arquivo de bloqueio para evitar essa situação. Um exemplo, da referida página:

if mkdir /var/lock/mylock; then
    echo "Locking succeeded" >&2
else
    echo "Lock failed - exit" >&2
    exit 1
fi

# ... program code ...

rmdir /var/lock/mylock
    
por 10.05.2012 / 10:59
2

As duas instâncias do seu script podem certamente interagir dessa maneira, fazendo com que o comando seja executado duas vezes. Isso é chamado de condição de corrida .

Uma maneira de evitar essa condição de corrida seria se cada instância pegasse seu arquivo de entrada movendo-o para outro diretório. Mover um arquivo (dentro do mesmo sistema de arquivos) é atômica . Mover os arquivos de entrada pode não ser desejável, e isso já está ficando um pouco complicado.

mkdir staging-$$ making-$$
for input in folder/*; do
  name=${x#folder/}
  staging=staging-$$/$name
  output=making-$$/$name
  destination=done/$name
  if mv -- "$input" "$staging" 2>/dev/null; then
    bin/myProgram "$staging" >"$output"
    mv -- "$output" "$destination"
    mv -- "$staging" "$input"
  fi
done

Uma maneira simples de processar os arquivos em paralelo usando uma ferramenta amplamente disponível é o make do GNU , usando o < href="http://www.gnu.org/software/make/manual/make.html#Parallel"> -j sinalizador para execução paralela . Aqui está um makefile para esta tarefa (lembre-se de usar abas para indentar comandos):

all: $(patsubst folder/%,done/%,$(wildcard folder/*))
done/%: folder/%
        ./bin/myProgram $< >[email protected]
        mv [email protected] $@

Execute make -j 3 para executar 3 instâncias em paralelo.

Veja também Quatro tarefas em paralelo ... como faço isso?

    
por 11.05.2012 / 02:51
1

Tenho a sensação de que você está realmente tentando executar vários trabalhos em paralelo e que o arquivo de bloqueio é simplesmente um meio para um fim.

Se você tem o link do GNU Paralelo instalado, você pode fazer isso:

parallel ./bin/myProgram ::: ./folder/*

Ele executará myProgram em cada núcleo em paralelo.

Você pode instalar o GNU Parallel simplesmente por:

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: link

    
por 14.05.2012 / 01:46
0

O problema com o bloqueio é que você precisa de um método que crie um bloqueio que seja ininterrupto (às vezes chamado atomar). Como Chris escreveu em sua resposta mkdir é uma operação ininterrupta (criar um arquivo não é uma operação desse tipo).

Existe também um comando de alto nível - usualmente oculto no procmail package: lockfile . Esse comando tem alguns recursos interessantes e pode ser facilmente usado em seus próprios scripts sem a necessidade de "reinventar a roda" (por exemplo, escrever sua própria função que bloqueia com base na criação de diretório).

    
por 11.05.2012 / 23:02