Помилка програми простого текстового лічильника

Давайте зробимо таку програму. Отже, у нас є якась сторінка, на
якої хочеться повісити лічильник. Обуді алгоритм:


Погодьтеся, програма проста, але може привести до помилки, що і показано
нижче.

<?

/ / Верхня частина сторінки

/ / Код лічильника:

$ Counter = file ("counter.txt"); / / прочитали файл в масив $ counter
$ F = fopen ("counter.txt", "w +"); / / відкрили файл на запис
fputs ($ f, $ counter [0] +1); / / записали "число + 1"
fclose ($ f); / / закрили файл
echo $ counter [0] +1; / / вивели число на екран

/ / Нижня частина сторінки

?>


Якщо викликати цю програму дуже часто, значення лічильника іноді буде
обнулятиметься. Це відбудеться через те, що в деякий момент програма
прочитає з файлу пусте значення, до якого потім додається одиниця
("Порожньо" + число 1 = число 1). Власне, це і є скидання лічильника.

Розглянемо докладно, коли це станеться. Уявіть, що в один момент
часу стартували 2 копії даного скрипта. Одночасно нічого ніде не
проиходит, в т.ч. і запуск скриптів, але час між запуском може бути дуже
маленьке. Процесор виконує скрипти з різною швидкістю, тобто ви не повинні
дивуватися тому, в якому порядку далі будуть розглядатися команди. Отже, хід
програми (на прикладі "скрипта N1" і "скрипта N2"):
























скрипт команда коментар (що зробить дана команда)
1 запуск першого скрипта
1 $counter=file(“counter.txt”); у змінній (масиві $ counter) тепер зберігатися поточне число лічильника.
Припустимо, там було 1234, тоді це число буде в змінної
$counter[0].
2 запуск другого скрипта
1 $f=fopen(“counter.txt”,”w+”);

  • відкриває файл
  • обнуляє його
  • якщо файл не був створений, створює його (якщо дозволять права). Але файл створений
    нами заздалегідь, цей варіант виключений.
























  • Як бачите, якщо 2 паралельно працюють скрипта, виконувати саме в такій
    послідовності, то файл буде обнулений. Якщо ви спробуєте це зробити,
    вилняя часту перезавантаження сторінки в браузері, то у вас швидше за все нічого не
    вийде. Щоб переконатися, що файл буде таки обнулений, скористайтеся утилітою ab
    (Яка вміє генерувати, протягом тривалого часу велике число,
    паралельних запитів до скіптам), або впишіть після кожної команди "sleep (1);"
    – Команду зупинки програми на 1 секунду, і понатискайте "Оновити" в браузері.
    У другому випадку ви це відразу і побачите.

    Щоб вирішити проблему, потрібно виключити небезпечний момент. Іншими словами треба
    заблокувати доступ до файлу лічильника, щоб всі інші паралельно запущені
    скрипти, призупинили свою роботу. Робиться це за допомогою flock, який
    блокує доступ з інших PHP-скриптів (але не з інших процесів ОС). Інші
    скрипти при спробі відкрити файл зупиняться і будуть чекати зняття блокування.

    <?

    / / Верхня частина сторінки

    / / Код лічильника:

    $ F2 = fopen ("counter.txt", "r"); / / щоб файл заблокувати, його треба відкрити
    / / Відкрили файл на читання
    flock ($ f2, 2); / / заблокували файл

    $ Counter = file ("counter.txt"); / / прочитали файл в масив $ counter
    $ F = fopen ("counter.txt", "w +"); / / відкрили файл на запис
    fputs ($ f, $ counter [0] +1); / / записали "число + 1"
    fclose ($ f); / / закрили файл
    echo $ counter [0] +1; / / вивели число на екран

    flock ($ f2, 3); / / зняли блокування (при закритті
    / / Знімається автоматично)
    fclose ($ f2); / / і закрили файл (при виході
    / / Закривається автоматично)

    / / Нижня частина сторінки

    ?>
    >


    Програму з блокуванням можна було б написати і в більш фарбуємо (короткому)
    вигляді, але і такий варіант зійде. Цифри "2" і "3" у функції flock позначають
    наступне:

    flock (дексріптор файлу, режим)

    режим:


    • 1 – інші процеси можуть відрити тільки в режимі читання
    • 2 – інші процеси нічого не можуть
    • 3 – зняти блокування

    Отже, на простому прикладі (простіше придумати важко) показані проблеми
    паралельного запуску скриптів.

    Схожі статті:


    Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

    Коментарів поки що немає.

    Ваш отзыв

    Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

    *

    *


    2 $counter=file(“counter.txt”); читає вміст порожнього файлу і записує в масив $ counter порожній
    масив. Змінна $ counter [0] не існує.
    1 fputs($f,$counter[0]+1); пише в файл число 1234 (тому що в $ counter [0] лежить число 1234)
    2 $f=fopen(“counter.txt”,”w+”); див. коментар вище
    1 fclose ($ f); і кінець роботи
    2 fputs($f,$counter[0]+1); записує у файл число 1, т.к результат складання неіснуючої змінної
    і числа 1 дорівнює числу 1
    2 fclose ($ f); і кінець роботи