Estou tentando entender por que não consigo implementar com sucesso um contador que flock
evita condições de corrida.
Copiei o flock
código desta resposta do SO .
O contador parece reiniciar aleatoriamente, embora eu não entenda por que isso deveria acontecer. Ele counter inc
deveria contar atomicamente para sempre.
Meu código foi criado para que eu possa processar vários arquivos de uma vez usando GNU parallel
, mas atualizar uma linha de status com cada arquivo processado que inclui um contador incremental.
O contador é salvo em um arquivo temporário na RAM criado por mktemp
.
Eu o cortei aqui para que seja apenas um loop infinito que deveria contar para sempre, mas ele continua reiniciando.
Alguém pode me explicar por que o contador zera às vezes?
#!/bin/bash
clear_line () {
echo -ne "\r\033[K"
}
counter () {
{
flock -s 200
read numfiles < "$countfile"
if [ "$1" = "inc" ]; then
((numfiles++))
fi
if [ "$1" = "rst" ]; then
numfiles=0
fi
if [ "$1" = "get" ]; then
echo "$numfiles"
fi
echo "$numfiles" > "$countfile"
} 200> "$countlockfile"
}
process () {
counter inc
currfileno=$(counter get)
clear_line
echo -n "$currfileno"
}
# from https://unix.stackexchange.com/a/29918/52247
tmpdir=
cleanup () {
trap - EXIT
if [ -n "$tmpdir" ] ; then rm -rf "$tmpdir"; fi
if [ -n "$1" ]; then trap - $1; kill -$1 $$; fi
}
tmpdir=$(mktemp -dt "$(basename $0).XXXXXXXX" --tmpdir=/run/user/$(id -u)) || exit 1
countfile=$(mktemp -t "counter.XXXXXXXX" --tmpdir=$tmpdir) || exit 1
countlockfile=$(mktemp -t "countlockfile.XXXXXXXX" --tmpdir=$tmpdir) || exit 1
trap 'cleanup' EXIT
trap 'cleanup HUP' HUP
trap 'cleanup TERM' TERM
trap 'cleanup INT' INT
export -f clear_line
export -f process
export -f counter
export countfile
export countlockfile
counter rst
while :
do
echo whatever
done | parallel process
O script basicamente tem uma corrida de dados . O GNU parallel invoca múltiplos processos para cada tarefa, então basicamente na sua função, entre as chamadas para
counter inc
ecounter get
, pode haver outro processo tentando incrementar ou zerar a contagem.Basicamente, você precisa de um bloqueio exclusivo com
flock -x
, que impede tanto a leitura quanto a escrita por outros processos enquanto o bloqueio é mantido.Porque sua
counter
função está lendo e escrevendo no arquivo na mesma seção crítica. Com um bloqueio compartilhado, vários processos podem entrar na seção crítica simultaneamente, cada um lendo o mesmo valor do arquivo, incrementando sua própria cópia e, então, escrevendo de volta, levando a reinicializações incorretas.Que tal deixar o GNU Parallel fazer isso?
Pessoalmente, gosto disso, onde obtenho a última saída de cada trabalho em execução:
Ou simplesmente, quando não me importo com a saída: