Eu quero executar vários scripts de shell Bash em paralelo. No entanto, quero evitar as condições da corrida. Quais comandos do Unix são realmente atômicos que eu poderia usar para esse fim, e como posso usá-los?
Eu quero executar vários scripts de shell Bash em paralelo. No entanto, quero evitar as condições da corrida. Quais comandos do Unix são realmente atômicos que eu poderia usar para esse fim, e como posso usá-los?
Respostas:
Se lockfile
não estiver instalado no seu sistema, ele mkdir
fará o trabalho: é uma operação atômica e falhará se o diretório já existir (contanto que você não adicione a -p
opção da linha de comandos).
create_lock_or_wait () {
path="$1"
wait_time="${2:-10}"
while true; do
if mkdir "${path}.lock.d"; then
break;
fi
sleep $wait_time
done
}
remove_lock () {
path="$1"
rmdir "${path}.lock.d"
}
#!/bin/bash
# Makes sure we exit if flock fails.
set -e
(
# Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
flock -x -w 10 200
# Do stuff
) 200>/var/lock/.myscript.exclusivelock
Isso garante que o código entre "(" e ")" seja executado apenas por um processo por vez e que o processo aguarde um bloqueio por muito tempo.
lockf(1)
.
lockf(1)
não funciona da maneira usada neste exemplo, no entanto. Não pode aceitar um número de descritor de arquivo como argumento.
O lockfile (1) parece um bom candidato, apesar de ser parte do pacote procmail , que talvez você ainda não tenha instalado em sua máquina. É um pacote bastante popular que deve ser empacotado para o seu sistema se ainda não estiver instalado. Três dos quatro sistemas que verifiquei o possuem e o outro está disponível.
Usá-lo é simples:
#!/bin/sh
LOCKFILE=$HOME/.myscript/lock
mkdir -p `dirname $LOCKFILE`
echo Waiting for lock $LOCKFILE...
if lockfile -1 -r15 $LOCKFILE
then
# Do protected stuff here
echo Doing protected stuff...
# Then, afterward, clean up so another instance of this script can run
rm -f $LOCKFILE
else
echo "Failed to acquire lock! lockfile(1) returned $?"
exit 1
fi
As opções que eu forneci fazem com que tente novamente uma vez por segundo por até 15 segundos. Solte a bandeira "-r" se desejar esperar para sempre.
A chamada do sistema mkdir()
é atômica nos sistemas de arquivos POSIX. Portanto, usando o mkdir
comando de forma a envolver exatamente uma chamada paramkdir()
atingir seu objetivo. (IOW, não use mkdir -p
). O desbloqueio correspondente é rmdir
claro.
Advertência emptor: mkdir()
pode não ser atômico nos sistemas de arquivos da rede.
rmdir
portanto, também atômica?
Talvez o comando lockfile faça o que você precisa.
lockfile ~/.config/mylockfile.lock
.....
rm -f important.lock
Se você estiver rodando apenas no Unix, use fifos. Você pode gravar registros de trabalho no fifo e fazer com que os processos sejam lidos neste arquivo, e seus leitores serão bloqueados no fifo.
Os arquivos de bloqueio estão ok, mas pelo que você descreve, eu usaria fifos
Como publicado aqui: " Corrigindo o bloqueio em scripts de shell? ", Usando a ferramenta FLOM (Free LOck Manager) , serializar comandos e scripts de shell se torna tão fácil quanto executar
flom -- command_to_serialize
O FLOM permite implementar casos de uso mais sofisticados (bloqueio distribuído, leitores / gravadores, recursos numéricos, etc.), conforme explicado aqui: http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/
make(1)
assuma o controle? (ou seja, faça ummake -j 9
se você tiver 8 núcleos)? Isso tem a vantagem adicional de intercalar o trabalho com granularidade mais fina.