Анимация
JavaScript
|
Главная Библионтека Примечание Возможно, вам на ум пришла аналогия с перекрестком, по одной дороге которого движется почти непрерывный поток машин, и поперечное движение при этом блокируется навсегда, - так что у водителей нет никаких шансов пробиться через сплошной поток. В реальном мире это действительно иногда происходит (потому-то любой светофор всегда представляет собой исключительную блокировку), но только не в мире PHP. Дело в том, что, если почти всегда активна разделяемая блокировка, операционная система все равно так распределяет кванты времени, что в некоторые из них можно "включить" исключительную блокировку. То есть "поток машин" становится не сплошным, а с "пробелами" - ровно такого размера, чтобы в них могли "прошмыгнуть" машины, едущие в перпендикулярном направлении. В листинге 15.3 представлена модель процесса, использующего разделяемую блокировку. i Листинг 15.3. Модель процесса с разделяемой блокировкой <? блокировку можно применять только в процессах, изменяющих файл? Ведь функция flock() не знает, что будет выполнено с файлом, для которого она вызвана. Однако этот метод довольно-таки неудачен, и вот по какой причине. Представьте, что процессов-читателей много, а писателей - мало, и к тому же писатели еще и вызываются, скажем, раз в пару минут, а не постоянно, как читатели. В случае использования исключительной блокировки для процессов-читателей, довольно интенсивно обращающихся к файлу, мы очень скоро получим целый их рой, висящий, недовольно гудя, в очереди, пока очередному процессу разрешат читать. Но ведь никакой "аварии" не случится, если один и тот же файл будут читать и сразу все процессы этого роя, правда? Ведь чтение из файла его не изменяет. Итак, предоставив исключительную блокировку для читателей, мы потенциально получаем проблемы с производительностью, перерастающие в катастрофу, когда процессов-читателей становится больше некоторого определенного порога. Второй (и лучший) способ подразумевает использование разделяемой блокировки. Процесс, который устанавливает этот вид блокировки, будет приостановлен только в одном случае: когда активен другой процесс, установивший исключительную блокировку. В нашем примере процессы-читатели будут "поставлены в очередь" только тогда, когда активизируется процесс-писатель. И это правильно. Посудите сами: зачем зажигать красный свет на перекрестке, если поперечного движения заведомо нет? Теперь давайте посмотрим на разделяемую блокировку читателей с точки зрения процесса-писателя. Что он должен делать, если кто-то читает из файла, в который он как раз собирается записывать? Очевидно, он должен дождаться, пока читатель не закончит работу. Иными словами, вызов flock($f,LOCK EX) обязан подождать, пока активна хотя бы одна разделяемая блокировка. Это и происходит в действительности. ( Совет ) Устанавливайте разделяемую блокировку, когда вы собираетесь только читать из файла, не изменяя его. Всегда используйте при этом режим открытия r, и никакой другой. Снимайте блокировку так рано, как только сможете. Блокировки с запретом "подвисания" Как следует из описания функции flock() , к ее второму параметру можно прибавить константу LOCK NB для того, чтобы функция не ожидала, когда программа может "двинуться в путь", а сразу же возвращала управление в основную программу. Это может пригодиться, если вы не хотите, чтобы ваш сценарий бесполезно простаивал, ожидая, пока ему разрешат обратиться к файлу. В эти моменты, возможно, лучше будет заняться какой-нибудь полезной работой - например, почистить временные файлы, память, или же просто сообщить пользователю, что файл заблокирован, чтобы он подождал и не думал, что программа зависла. Вот пример использования исключительной блокировки в совокупности с LOCK NB: $f=fopen("file.txt","a+"); while(!flock($f,LOCK EX+LOCK NB)) { echo "П1таемся получить доступ к файлу <br>"; sleep(1); ждем 1 секунду Работаем Эта программа основывается на том факте, что выход из flock() может произойти либо в результате отказа блокировки, либо после того, как блокировка будет установлена - но не до того! Таким образом, когда мы наконец-то дождемся разрешения инициализация . . . $f=fopen($f,"r") or die("Не могу открыть файл на чтение!"); flock($f,LOCK SH); ждем, когда процессы-писатели угомонятся В этой точке можем быть уверен:, что эта программа работает с файлом, когда ни одна другая программа в него не пишет . . . flock($f,LOCK UN); говорим, что мы больше не будем работать с файлом fclose($f); Завершение . . . ?> ! Листинг 15.4. Простейший текстовый счетчик <? $f=fopen("counter.dat","a+"); flock($f,LOCK EX); Говорим, что дальше будем работать только $count=fread($f,100); Читаем значение, сохраненное в файле @$count=$count+1; Увеличиваем его на 1 (пустая строка = 0) ftruncate($f,0); Стираем файл fwrite($f,$count); Запис1ваем новое значение fflush($f); Сбрасываем файловый буфер flock($f,LOCK UN); Отключаемся от блокировки fclose($f); Закрываем файл echo $count; Печатаем величину счетчика ?> Здесь мы применяем только исключительную блокировку, потому что каждый раз, когда нам нужно вывести на экран счетчик, его также нужно и увеличить. доступа к файлу и произойдет выход из цикла while, мы уже будем иметь исключительную блокировку, закрепленную за нашим файлом. Пример счетчика Давайте напоследок рассмотрим классический пример, когда без блокировки файла не обойтись. Если вы уже имели некоторый опыт в Web-программировании, то вы, наверное, уже догадываетесь, что речь пойдет о проблеме, возникающей при написании сценария счетчика. Итак, нам нужен сценарий, который бы при каждом своем запуске увеличивал число, хранящееся в файле, и выводил его в браузер. Несложная, казалось бы, задача сильно осложняется тем, что при большой посещаемости сервера могут быть запущены сразу несколько процессов-счетчиков, которые попытаются обратиться к одному и тому же файлу. Если не принять мер, это приведет к тому, что счетчик рано или поздно "об-нулится". Далее следует сценарий, использующий блокировку для предотвращения указанной проблемы. 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |