Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 [ 75 ] 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

555-6611" в массив. а в это время первый пользователь записывает в файл базы

данных содержимое массива @PHONEL, в котором уже есть запись Дмитрий 555-1212".

Когда же копия программы второго пользователя "добирается" до пятого шага, она "затирает" данные, записанные первым пользователем. Таким образом, в окончательном варианте базы данных на диске будет присутствовать запись Крий 555-6611", но не будет записи "Дмитрий 555-1212", что является очевидной ошибкой.

На самом деле проблема гораздо глубже, чем мы только что ее описали. Приведенный выше пример был явно упрощен. Мы не учли тот факт, что функция writedatal) не может мгновенно открыть файл и записать в него данные. В многозадачных операционных системах ядро обычно прерывает выполнение программы в середине цикла записи и. отдает управление другому процессу. По прошествии нескольких десятков миллисекунд (но не мгновенно!) управление снова возвращается программе, которая записывала данные на диск. Таким образом, возможна ситуация, когда обе программы одновременно попытаются записать разные данные в один и тот же файл. Все это может привести к тому, что часть данных будет испорчена или структура файла будет полностью нарушена.

Описанная выше работа программы в многозадачной среде чем-то напоминает

гонки "Формулы 1". Отлаживать многозадачные и многопоточные приложения очень сложно, поскольку работа таких программ зависит как от количества запушенных копий, так и от многих других факторов. Поэтому ошибки в многопоточных программах не всегда очевидны и их очень трудно выявить и локализовать.

Как мы уже видели, одновременная запись данных в один и тот же файл несколькими программами - весьма опасная вещь. Однако это совсем не означает, что подобное невозможно. Процесс одновременной записи в файл синхронизируется механизмом, называемым блокировкой. Таким образом, блокировка файлов предотвращает одновременную запись в файл со стороны нескольких программ в один и тот же момент времени,

Блокировка- файлов вызывает сразу несколько проблем, причем основная из них связана с тем, что в разных файловых и операционных системах используются разные

механизмы блокировки. Поэтому в следующих разделах мы расскажем о том, как преодолеть все эти проблемы.

Блокировка в UNIX и Windows NT

для блокировки файлов в системах UNIX и Windows NT используется функция Perl flock, в которой реализован так называемый совещательный механизм блокировки. Это означает, что любая программа, которая хочет записать что-либо в файл, должна перед этим вызвать функцию flock и убедиться, что никакая другая программа в данный момент времени ничего не пишет в этот же файл. Естественно, при желании любая программа сможет в любой момент записать данные в файл, не прибегая к средствам блокировки. В этом заключается отличие совещательного механизма блокировки от принудительного.

С механизмом совещательной блокировки вы должны быть уже хорошо знакомы. Вспомните, как работает светофор на перекрестке. Красный сигнал светофора запрещает движение транспорта и таким образом препятствует движению машин в пересекающихся направлениях. Но регулировка с помощью светофора работает только тогда, когда все водители строго соблюдают правила дорожного движения. То же самое можно сказать и о механизме блокировки файлов. Любая программа, в которой не исключена возможность доступа к файлам одновременно с другими программами, должна использовать функцию flock для предотвращения нежелательных последст-

242 П. углубляемся e Perl



вий Следовательно, механизм совещательной блокировки не препятствует доступу нескольких программ к файлу, а предотвращает только возможность получения права доступа к файлу.

У функции Поск предусмотрены два параметра - дескриптор файла и тип блокировки, как показано ниже.

use Fcntl qw{;flock); ПосУ.1дескриптор файла, тип 6локвровкЕ);

Функция flock возвращает истинное значение, если блокировка файла была успешно выполнена. В противном случае возвращается ложное значение. Иногда вызов функции flock приостанавливает выполнение программы до момента снятия других блокировок. Ниже мы остаповимся па этом более подробпо. Директива use Fcntl flock) позволяет использовать символические имена вместо трудно запоминаемых

цифр при определении типа блокировки.

Существуют два вида блокировки: совместно используемая и монопольная. Обычно при чтении файла применяется совместно используемый тип блокировки, а при записи - монопольный. Если какой-либо процесс получил монопольный доступ к файлу, он может выполнять с ним любые действия, поскольку в этот момент никакой другой процесс не сможет обратиться к файлу. Однако многие процессы могут совместно использовать один и тот же файл для чтения, если никакой другой процесс не запросил монопольный доступ к этому файлу.

Ниже мы приведем несколько значений параметра типа блокировки для функции flock

SH - это значение определяет совместно используемый тип блокировки

файла. Если какой-либо другой процесс запросил мопопольную блокировку, вызов функции flock с параметром приведет к приостановке выпол-

нения программы до момента снятия монопольной блокировки. И только

после того, как мопопольпая блокировка будет снята, выполняется совместно используемый тип блокировки.

ЕХ - это значение определяет монопольный тип блокировки файла, открытого для записи. Если какие-либо процессы ранее заблокировали этот

файл (для монопольного или совместно используемого доступа), вызов функции flock с параметром LOCK EX приведет к приостановке выполнения программы до момента снятия всех блокировок.

UN - это значение позволяет отменить блокировку файла. Следует отметить, что используется оно сравнительно редко, поскольку при закрытии файла автоматически все не сохраненные данные записываются на диск и

отменяется действие всех блокировок. Учтите, что принудительное снятие

блокировки с открытого файла может привести к потере данных или нарушению структуры файла.

Блокировки файла, выполненные с помощью функции flock, автоматически отменяются при закрытии файла либо при завершении работы программы (даже если программа завершилась с ошибкой).

При выполнении блокировки файла, который открыт для чтения и записи, могут возникнуть некоторые нюансы. Проблема заключается в том, что открытие файла и выполнение последующей блокировки - двухступенчатый процесс. Другими словами, для того чтобы заблокировать файл, сначала его нужно открыть. Поэтому, если вы от-



кроете файл с помощью оператора а затем сразу же попытаетесь

его заблокировать с помошью функции flock, содержимое файла будет затерто перед

выполнением блокировки (поскольку при открытии использовался оператор усечения файла >). Таким образом, при открытии файла для записи вы можете случайно модифицировать файл, который был заблокирован другими процессами.

Для решения описанной выше проблемы используется так называемый семафорный файл (обычный файл, содержимое которого не имеет особого значения; он используется только для выполнения блокировок).

Перед использованием семафорного файла вы должны определиться с его именем. Для выполнения и снятия блокировок используются уже знакомые нам функции, как показано в листинге 15.3. Этот фрагмент кода нельзя назвать законченной программой, но его вполне можно включить в другой проект как самостоятельную часть.

lFcntl qw(:floc]c); I Для семафсра жшю вьрать любое ния Ssemaphore file="/tmp/Bample.sem";

\ Функция для блокировки (снедает разблокировки бесконечно долго) subgetjock {

openSEM, ">5semaphore file)

di ябха при созданин сенафора: $!";

flcick(SEM, LOCK EX} jj die "Ошибка при блокировке: $1";

I Функция для снятия блокировки sub release lock { close(SEH};

>

Описанные в листинге 15.3 функции для блокировки и разблокировки можно использовать в любом месте программы, где пужпо, чтобы некоторый участок кода выполнялся строго последовательно (без запуска конкурентных процессов), а не только для блокировки файлов при их чтении и записи. Например, даже если приведенный ниже фрагмент кода будет запущен параллельно сразу несколькими процессами, сообщение будет выводиться на экран в текущий момент времени только одним процессом.

get lock{); I Яйэу1, хгка освобсдотся сзаувфср podiit "НэИо, World 1\п-; release loclt:(); J ОсвобсияУ! С!аувф:р

Функции get lock() и release lock{), рассмотренные выше, мы будем при необходимости использовать далее по всей книге для выполнения блокировки файлов.

Не рекомендуется устанавливать блокировку перед вводом информации с клавиатуры или для выполнения любой другой медленной операции, поскольку при этом будут приостановлены все другие процессы, ожидающие снятия блокировки. Старайтесь блокировать критичные участки кода только на короткий промежуток времени.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 [ 75 ] 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122