Если я одновременно запускаю следующий скрипт в трех отдельных терминалах, я получаю файл CSV, в котором отсутствует от 25 до 50 строк (общее количество, как ожидается, будет 3001 строк после завершения всех сценариев).
<?php
// Run this command in three separate consoles, concurrently.
// - Run the first with php -f ./test.php 1
// - Run the second with php -f ./test.php 2
// - Run the second with php -f ./test.php 3
$prefix = $argv[1];
for ($value = 0; $value < 1000; ++$value) {
$sleep_amount = rand(50000, 500000);
print "Sleeping for {$sleep_amount} micro-seconds...";
usleep($sleep_amount);
print "{$value}\n";
lock_and_write($prefix, $value);
}
function lock_and_write($prefix, $value) {
$file_path = "test.csv";
$file_handle = fopen($file_path, 'c');
if ($file_handle === FALSE) {
throw new \RuntimeException(
sprintf('Could not open CSV file for writing [%s].', $file_path)
);
}
try {
flock($file_handle, LOCK_EX);
fseek($file_handle, 0, SEEK_END);
$write_header = (ftell($file_handle) === 0);
if ($write_header) {
fputcsv($file_handle, array('prefix', 'value'));
}
fputcsv($file_handle, array($prefix, $value));
}
finally {
fflush($file_handle);
flock($file_handle, LOCK_UN);
fclose($file_handle);
}
}
С другой стороны, кажется, что следующий скрипт работает нормально (он использует отдельный файл блокировки):
<?php
// Run this command in three separate consoles, concurrently.
// - Run the first with php -f ./test.php 1
// - Run the second with php -f ./test.php 2
// - Run the second with php -f ./test.php 3
$prefix = $argv[1];
for ($value = 0; $value < 1000; ++$value) {
$sleep_amount = rand(25000, 500000);
print "Sleeping for {$sleep_amount} micro-seconds...";
usleep($sleep_amount);
print "{$value}\n";
lock_and_write($prefix, $value);
}
function lock_and_write($prefix, $value) {
$file_path = "test.csv";
$lock_file = "{$file_path}.lockfile";
$lock_handle = fopen($lock_file, 'w');
if ($lock_handle === FALSE) {
throw new \InvalidArgumentException(
sprintf('Could not open CSV lock file [%s].', $lock_file)
);
}
if (flock($lock_handle, LOCK_EX)) {
$file_handle = fopen($file_path, 'c');
if ($file_handle === FALSE) {
throw new \RuntimeException(
sprintf('Could not open CSV file for writing [%s].', $file_path)
);
}
try {
if (fseek($file_handle, 0, SEEK_END) !== 0) {
throw new \RuntimeException(
sprintf('Could not open CSV file for writing [%s].', $file_path)
);
}
$write_header = (ftell($file_handle) === 0);
if ($write_header) {
fputcsv($file_handle, array('prefix', 'value'));
}
fputcsv($file_handle, array($prefix, $value));
}
finally {
fflush($file_handle);
fclose($file_handle);
flock($lock_handle, LOCK_UN);
fclose($lock_handle);
}
}
else {
echo "Failed to acquire lock!";
}
}
Почему требуется отдельный файл блокировки? Является ли блокировка добавляемого файла ненадежной? Есть ли задокументированная известная проблема между flock()
и fputcsv()
?
В случае, если вам интересно, почему я не использую режим добавления (a
), у меня была одна теория о том, почему это не удалось, потому что Я подумал, что, возможно, fopen()
ищет в конце файла с момента открытия файла , а не ищет в тот момент, когда запись происходит после получения блокировки.