Предотвращение взаимоблокировки, вызванной стаей

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

$loop = 10000;
$sleep = 500000;
$i =0;

while($i < $loop) {

    $mtime = microtime();
    $mtime = explode(" ",$mtime);
    $mtime = $mtime[1] + $mtime[0];
    $starttime = $mtime; 

    $handler = fopen($file,"a+");
    if($handler) {
    if (flock($handler, LOCK_EX)) {
        $mtime = microtime();
        $mtime = explode(" ",$mtime);
        $mtime = $mtime[1] + $mtime[0];
        $endtime = $mtime;
        $totaltime = ($endtime - $starttime); 

        fwrite($handler,"Script 1 took $totaltime secs\n");
    }

    flock($handler, LOCK_UN);
    fclose($handler);
}
$i++;
usleep($sleep);
}

Я не могу использовать LOCK_NB, потому что он не работает в Windows. Код работает нормально, если существует менее 13 процессов, выполняющих указанный выше код одновременно. Как мне справиться с этой тупиковой ситуацией?


person Maximus    schedule 24.04.2011    source источник
comment
Тупик между несколькими процессами PHP не должен замораживать всю вашу систему. Вы уверены, что это тупик?   -  person Matti Virkkunen    schedule 24.04.2011
comment
Да, пробовал два раза и в итоге перезагрузил систему. Я создал 40 процессов php.   -  person Maximus    schedule 24.04.2011
comment
LOCK_NB, кажется, работает в Windows, несмотря на то, что говорится в документации: bugs.php.net/ ошибка.php?id=54129   -  person magma    schedule 24.04.2011
comment
Согласен, что тупика быть не должно, а какое-то ожидание процессов. Если ваша система зависает, это может быть что-то еще. Зачем добавлять в файл, а не использовать базу данных?   -  person Erik    schedule 24.04.2011
comment
Я запускаю тот же код в CentOS, и с этим проблем нет, так что это может быть несовместимость Windows с flock.   -  person Maximus    schedule 24.04.2011
comment
Если вы сделаете $starttime = microtime(TRUE), вы получите значение времени в виде простого числа с плавающей запятой, что избавит вас от шага разнесения/добавления.   -  person Marc B    schedule 24.04.2011
comment
Джейсон, ты когда-нибудь выяснял, что вызвало твой очевидный тупик?   -  person Peter    schedule 14.06.2013


Ответы (4)


Читая ваш код, я думаю, вам следует переместить flock($handler, LOCK_UN); внутрь условного блока if (flock($handler, LOCK_EX)) {}.

Чтобы точно выяснить, что и где застревает, я бы добавил отметку даты (и сброс, чтобы ничего не застревало в выходном буфере) вывод отладки до и после каждого вызова flock(), fopen(), fwrite() и fclose() и перенаправил вывод из каждый экземпляр сценария в свой собственный файл.

Затем, после заморозки + перезапуска, вы можете просмотреть конец каждого файла и посмотреть, что делал каждый скрипт при перезапуске. Сравнив метки дат, вы сможете увидеть, какие скрипты зависли первыми.

person Peter    schedule 07.09.2012
comment
Перемещение LOCK_UN в if точно предотвращает тупиковую блокировку. Находясь снаружи, может случиться так, что процессы тратят LOCK_UN на несуществующие блокировки и, следовательно, складывают заблокированные процессы. - person Markus Malkusch; 20.11.2014

Hy

Попробуйте с file_put_contents():

<?php

$file = 'file.txt';

$str = "some text\n";

file_put_contents($file, $str, FILE_APPEND | LOCK_EX);

?>

person CoursesWeb    schedule 25.04.2011

Прежде чем вы напишете еще немного php-кода, я рекомендую вам использовать приложение AB (бенчмарк apache), которое поставляется с apache, и имитировать высокую нагрузку на вашем локальном хосте EX>

ab -n 1000 -c 200 http://localhost/your.php

При этом вы можете имитировать 200 одновременных пользователей и 1000 запросов.

person Yuri    schedule 12.09.2011

Попробуйте «защитить» LOCK_EX с помощью дополнительного механизма блокировки mkdir(), как описано здесь: https://stackoverflow.com/a/7927642/318765

Пример:

<?php
$file = 'deadlock.txt';
$loop = 10000;
$sleep = 500000;
$i = 0;
while ($i < $loop) {
    $starttime = microtime(true);
    $handler = fopen($file, 'a+');
    if ($handler) {
        if (!file_exists($file . '_lock')) {
            if (mkdir($file . '_lock')) {
                if (flock($handler, LOCK_EX)) {
                    $endtime = microtime(true);
                    $totaltime = ($endtime - $starttime);
                    $totaltime = number_format($endtime - $starttime, 10);
                    if ($totaltime > 1) {
                        break;
                    }
                }
                flock($handler, LOCK_UN);
                fclose($handler);
                rmdir($file . '_lock');
            }
        }
    }
    $i++;
    usleep($sleep);
}
?>

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

В Linux я использую этот трюк mkdir() без flock(), так как он атомарный. Я не знаю, работает ли это и в Windows.

person mgutt    schedule 08.09.2014