Если вы решаете задачу конкурентного доступа к файлу (или какому то другому ресурсу) в PHP, и решили использовать flock() для этой цели, то вам понадобится верно представлять как работает механизм блокировки.
Представим себе, что есть разные программные потоки, одни из которых читают данные — «читатели», а другие их меняют — «писари». Ясно, что «читатель» должен дождаться пока операции записи завершатся. При этом несколько «читателей» не мешают друг другу.
Семафор
Чтобы блокировка работала, необходимо использовать один и тот же способ блокировки. Иными словами, flock() нужно вызывать как при чтении, так и при записи.
В случае с «семафорным» файлом код может выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//поток читателя $semHandler = fopen($filename, 'r'); if (flock($semHandler, LOCK_SH)) { //выполняем операции чтения } flock($semHandler, LOCK_UN); fclose($semHandler); //поток писаря $semHandler = fopen($filename, 'r'); if (flock($semHandler, LOCK_EX)) { //выполняем операции записи } flock($semHandler, LOCK_UN); fclose($semHandler); |
Т.е. мы проводим блокировку некого файла-семафора, а запись и чтение проводится с другими объектами.
Блокировка
Если нужно провести блокировку объекта, с которым проводятся непосредственные операции чтения-записи, то открывать файл «писарем» потребуется в специальном режиме. Режимы ‘w’ и ‘w+’ тут не подходят, т.к. до получения блокировки, содержимое файла менять нельзя.
1 2 3 4 5 6 7 8 9 10 11 12 |
//поток писаря $fh = fopen($filename, 'c'); if (flock($fh, LOCK_EX)) { //блокировка получена, теперь можно менять файл //удалили данные ftruncate($fh, 0); //перезаписали fwrite($fh, $data); } //сняли блокировку flock($fh, LOCK_UN); fclose($fh); |
Для полноты примера, приведу код потока-читателя:
1 2 3 4 5 6 7 8 9 |
//поток читателя $fh = fopen($filename, 'r'); if (flock($fh, LOCK_SH)) { //блокировка получена, теперь можно читать данные $data = @fread($fh, filesize($filename)); } //сняли блокировку flock($fh, LOCK_UN); fclose($fh); |
Блокировка с тайм-аутом
По умолчанию flock() ждет получения блокировки. Сколько продлится ожидание не известно, но можно использовать флаг LOCK_NB, чтобы не ждать, а самим решать, что делать дальше.
В данном примере ожидание продлится не более 50 мсек.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//поток читателя $fh = fopen($filename, 'r'); $timeout_secs = 50; $got_lock = true; while (!flock($fh, LOCK_SH | LOCK_NB, $wouldblock)) { if ($wouldblock && --$timeout_secs > 0) { sleep(1); } else { $got_lock = false; break; } } if ($got_lock) { //дождались блокировки, читаем данные $data = @fread($fh, filesize($filename)); flock($fh, LOCK_UN); } else { //доступ не удалось получить за отведенное время ... } fclose($fh); |
Если еще остались вопросы — пишите в комментариях. :)