должен ли LOCK_EX для чтения и записи быть атомарным? - PullRequest
21 голосов
/ 04 февраля 2011
file_put_contents ( "file", "data", LOCK_EX )

для записи (что означает - aquire lock and write)

file_get_contents ( "file", LOCK_EX )

для чтения (что означает - заблокировать, а затем прочитать)

будет ли выбрасывать исключение? поднять ошибку? блокировать до блокировки? или хотя бы - должно ли ? есть ли шанс, что php будет вести себя так же в один прекрасный день?

РЕДАКТИРОВАТЬ: я знаю, что можно использовать переименование - я хотел бы знать ответ на этот вопрос ...

Ответы [ 2 ]

42 голосов
/ 04 февраля 2011

Поскольку этот ответ длинный, вот краткое изложение: Нет, file_get_contents() не является атомарным, поскольку не учитывает консультативные блокировки .

О блокировках файлов в PHP:

В PHP, на платформе * nix блокировка файловой системы носит рекомендательный характер.Согласно документам (выделение у меня):

PHP поддерживает переносимый способ блокировки файлов целиком (то есть все программы доступа к должны использовать одно и то же).способ блокировки или не будет работать ).По умолчанию эта функция будет блокироваться до тех пор, пока не будет запрошена блокировка;этим можно управлять (на платформах, отличных от Windows) с помощью параметра LOCK_NB, описанного ниже.

Так что, пока все процессы, которые обращаются к файлу, используют этот метод блокировки, выхорошо.

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

Единственное реальное исключение - если вы используете специальную опцию монтирования -o mand в файловой системе, чтобы включить обязательную блокировку (но на самом деле она используется не очень часто).и может иметь снижение производительности).

Прочтите Блокировка файлов для получения дополнительной информации.А именно раздел под Unix :

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

Итак, в заключение, использование LOCK_EX создаст консультативную блокировку файла.Любая попытка чтения файла блокируется, только если читатель уважает и / или проверяет блокировку.Если они этого не делают, блокировка будет игнорироваться (так как это может быть).

Попробуйте.В одном процессе:

file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);

И пока он спит, назовите это:

$f = fopen('test.txt', 'a+');
fwrite($f, 'foobar');
fclose($f);

Вывод будет foobar ...

О file_get_contentsв частности:

К вашему другому конкретному вопросу, во-первых, нет параметра LOCK_EX для file_get_contents.Таким образом, вы не можете передать это.

Теперь, глядя на исходный код , мы можем увидеть функцию file_get_contents, определенную в строке 521. Нет вызовов для внутренней функцииphp_stream_lock как и при передаче file_put_contents('file', 'txt', LOCK_EX);, определенной в строке 589 того же файла.

Итак, давайте проверим это:

В file1.php:

file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);

В file2.php:

var_dump(file_get_contents('test.txt'));

При запуске file2.php немедленно возвращается.Так что нет, не похоже, что file_get_contents вообще учитывает блокировки файлов ...

3 голосов
/ 04 февраля 2011

Теоретические вопросы работают гораздо лучше на Программистах , чем здесь.

На данный момент PHP не поддерживает атомарную блокировку файлов.

Проще говоря, PHP неподдерживает комбинированную операцию fopen и flock, поэтому у другого процесса всегда будет небольшое окно для блокировки файла, который ваш процесс также открыл до того, как ваш процесс сможетзаблокируйте его.

Сказав, что flock будет по умолчанию блокироваться до снятия блокировки.Имейте в виду, что примечание ircmaxell о консультативных блокировках в Linux / BSD.

Примечание: Для процесса чтения вы можете сделать его LOCK_SH вместо LOCK_EX, чтобы несколько потоков чтения моглизаблокируйте это в то же время.Запись должна всегда выполняться с использованием LOCK_EX или риска повреждения данных.

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

...