Как реализовать блокировку в многопроцессорной системе?

Мы параллельно запускаем множество проектов jenkins. Мы используем python и решили управлять виртуальными средами с помощью pyenv. К сожалению, у pyenv есть известная состояние гонки. Чтобы обойти проблему, я хотел бы реализовать блокировку на уровне процесса. Что я хочу сделать, так это:

lock some resource (a file?)
do my pyenv stuff
unlock the resource

Мои скрипты написаны на bash. Как реализовать блокировку/разблокировку ресурсов в bash?


person blueFast    schedule 22.12.2016    source источник


Ответы (2)


Таким образом, вашим другом в мире Unix, когда вам нужна блокировка между процессами, является команда под названием flock. Он реализован как атомарная операция на уровне ОС и чрезвычайно полезен для такого рода вещей. Вы можете прочитать подробнее об этом здесь. Вот как вы можете его использовать:

  # Wait for lock on  (fd 222) for 10 seconds
  (flock -w 10 222 || exit 1

  {
      # Do the operations you want to here
  }) 222>/path/to/lockfile 

Здесь есть несколько хитростей. Во-первых, обычно при использовании перенаправления вывода bash сначала открывает файл, прежде чем даже пытаться выполнить блокировку. Однако здесь, поскольку у нас есть (), bash сначала запускает подоболочку, первой командой которой является flock. flock попытается получить блокировку дескриптора файла 222. Затем Flock заблокирует дескриптор файла. После блокировки файлового дескриптора запускается код в {}. После этого содержимое файлового дескриптора 222 записывается в файл блокировки (т. е. ничего), файл закрывается и блокировка снимается. Это похоже на C, где закрытие файла освобождает блокировку. Конечно, никто не объясняет это лучше, чем прославленный @CharlesDuffy (наконечник шляпы @codeforester), который объясняет, что происходит здесь.

person 2ps    schedule 22.12.2016
comment
Хороший. Почему фигурные скобки? - person blueFast; 22.12.2016
comment
Без особой причины. Мне просто нравится иметь их там, чтобы отделить код, выполняющий блокировку, от кода, выполняющего настоящую работу. - person 2ps; 22.12.2016
comment
@2ps: я получаю синтаксическую ошибку при запуске вашего кода. - person codeforester; 22.12.2016
comment
Мой плохой, лишний интервал, когда я печатал. - person 2ps; 22.12.2016
comment
@codeforester: вы правы, у меня были лишние пробелы вокруг >. Спасибо, что поймали это. - person 2ps; 22.12.2016
comment
Связанный пост: stackoverflow.com/questions/28227739/ - person codeforester; 22.12.2016
comment
Если я прав, пробел строго разрешен после знака перенаправления, верно? Так что это было бы синтаксически и семантически правильно: 222> /path/to/lockfile - person blueFast; 22.12.2016
comment
Не могли бы вы объяснить в своем посте, как это работает? Когда перенаправление создает файл и когда файл удаляется? В каком порядке выполняются эти операторы: создание файла, попытка блокировки, снятие блокировки... - person blueFast; 22.12.2016

Я бы подумал об использовании символических ссылок вместо обычных файлов, потому что создание символических ссылок является атомарной операцией. Итак, мы могли бы сделать это:

lockfile=/path/to/lock.file
# lockfile is a symlink that points the string that holds the PID of the locking process
ln -s $$ $lockfile 2>/dev/null
if [[ $? == 0 ]]; then
    # got the lock - ln -s will fail if symlink exists already
else
    otherprocess=$(readlink $lockfile)
    if [[ $otherprocess != $$ ]]; then
        ps -p $otherprocess 2>/dev/null
        if [[ $? != 0 ]]; then
            # stale lock; remove and lock again
            # this can result in race conditions
            # probably, we can make the lock procedure as a function that is shared by concurrent bash scripts and have a random sleep before we remove the stale lock and proceed
         fi
    fi
fi
person codeforester    schedule 22.12.2016
comment
Красиво, но слишком специально. Кажется, для этого уже существует команда: flock - person blueFast; 22.12.2016
comment
@delavnog: я видел реализации, использующие символическую ссылку для блокировки. Но я согласен, что стадо лучше. - person codeforester; 22.12.2016