Je veux exécuter plusieurs scripts shell Bash en parallèle. Cependant, je veux éviter les conditions de course. Quelles commandes Unix sont vraiment atomiques que je pourrais utiliser à cette fin et comment puis-je les utiliser?
Je veux exécuter plusieurs scripts shell Bash en parallèle. Cependant, je veux éviter les conditions de course. Quelles commandes Unix sont vraiment atomiques que je pourrais utiliser à cette fin et comment puis-je les utiliser?
Réponses:
Si lockfile
n'est pas installé sur votre système, mkdir
le travail sera alors effectué: il s'agira d'une opération atomique qui échouera si le répertoire existe déjà (tant que vous n'ajoutez pas le -p
commutateur de ligne de commande).
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
Cela garantit que le code entre "(" et ")" est exécuté uniquement par un processus à la fois et qu'il attend trop longtemps le verrouillage.
lockf(1)
.
lockf(1)
ne fonctionne pas de la manière utilisée dans cet exemple, cependant. Il ne peut pas prendre un numéro de descripteur de fichier en argument.
lockfile (1) semble être un bon candidat, mais sachez que cela fait partie du paquet procmail , que vous n'avez peut-être pas encore installé sur votre ordinateur. C'est un paquet assez populaire pour qu'il soit packagé pour votre système s'il n'est pas encore installé. Trois des quatre systèmes que j'ai vérifiés l'ont et l'autre le dispose.
Son utilisation est simple:
#!/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
Les options que je vous ai données permettent de réessayer une fois par seconde pendant 15 secondes maximum. Supprimez l'indicateur "-r" si vous souhaitez attendre indéfiniment.
L'appel système mkdir()
est atomique sur les systèmes de fichiers POSIX. Donc, en utilisant la mkdir
commande de telle sorte qu'elle implique exactement un appel, vous mkdir()
atteindriez votre but. (IOW, ne pas utiliser mkdir -p
). Le déverrouillage correspondant est rmdir
bien sûr.
Caveat emptor: mkdir()
pourrait ne pas être atomique sur les systèmes de fichiers du réseau.
rmdir
donc aussi atomique?
Peut-être que la commande lockfile fera ce dont vous avez besoin.
lockfile ~/.config/mylockfile.lock
.....
rm -f important.lock
Si vous utilisez uniquement Unix, utilisez fifos. Vous pouvez écrire des enregistrements de travail dans le fifo et faire en sorte que les processus soient lus à partir de ce fichier. Vos lecteurs bloquent alors sur le fifo.
Les fichiers de verrouillage sont ok, mais pour ce que vous décrivez, j'irais avec fifos
Comme indiqué ici: " Corriger le verrouillage des scripts de shell? ", L’utilisation de l’ outil FLOM (Free LOck Manager) , la sérialisation des commandes et des scripts de shell sont aussi simples qu’exécuter
flom -- command_to_serialize
FLOM vous permet d'implémenter des cas d'utilisation plus souples (verrouillage distribué, lecteurs / rédacteurs, ressources numériques, etc.), comme expliqué ici: http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/
make(1)
de prendre le relais? (c.-à-d.make -j 9
si vous avez 8 cœurs)? Cela présente l’avantage supplémentaire d’entrelacer les travaux avec une granularité plus fine.