Статьи

Синхронизация в PHP

Что означает синхронизация

Мы говорим о синхронизации процесса здесь. Синхронизация — это применение определенных механизмов для обеспечения того, чтобы два одновременно выполняющихся процесса не выполняли определенные части программы одновременно [ Wikipedia ]. В более общем смысле, конкретные части — это те, которые обращаются к структурам данных, совместно используемым различными процессами, и которые не должны быть доступны процессу, в то время как другой находится на полпути через их изменение, таким образом сохраняя инварианты (которые процессы полагаются в своей логике) такими данными доволен.

Например, разные процессы могут получить значение общей переменной, увеличить его и сохранить это новое значение в исходном местоположении. Если при разделении времени выполняется умеренно большое количество процессов, два или более из них могут получить значение переменной одновременно, что приводит только к одному приращению для двух исполнений. Говорят, что такого рода операции с данными должны быть атомарными: определенным блокам кода должно быть разрешено выполняться полностью до того, как будет выполнена любая другая часть, которая касается той же структуры данных (по существу, которая изменяет то же состояние).

В PHP

Скрипт PHP по своей природе невосприимчив к внутренним проблемам синхронизации, в отличие от других языков, благодаря своей простой архитектуре. Нет встроенной поддержки для создания нескольких потоков, которые совместно используют одни и те же переменные PHP и которые одновременно работают в одном и том же сценарии PHP. Вы можете использовать exec () для запуска внешней программы в фоновом режиме или других сценариев PHP, но она не разделяет область действия исходного сценария (как это сделал бы файл include () d), и это, как правило, хорошая идея, если бы не конкретные случаи использования, которые включают сложную интеграцию внешних систем.

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

Но имейте в виду, что в PHP клиент ожидает завершения сгенерированной страницы, пока не завершится сценарий PHP и элемент управления не вернется на HTTP-сервер; если у вас есть бизнес-логика, которая ожидает внешних событий или ресурсов, и вы хотите тем временем вернуть элемент управления пользователю, вам не нужен новый поток (который в текущей архитектуре вам тоже приходилось ждать): вы нужно на аутсорсинг эту работу. Zend Server Job Queue и, проще говоря, cron позволяют настраивать задачи для асинхронного выполнения. Например, вы можете запланировать PHP-скрипт, который выполняет тяжелые вычисления и должен выполняться один раз в час. На мой взгляд, это многопоточность, как задумано в PHP.

Вне PHP

Даже если PHP по своей природе не имеет общего доступа и не представляет классических условий гонки для переменных из-за его ограничений, каждый тип общего ресурса является кандидатом на проблемы синхронизации. Поскольку PHP-скрипты недолговечны и не сохраняют состояния, целью этого анализа являются ресурсы, которые поддерживают состояние для PHP-скриптов: разные сценарии или даже один и тот же сценарий могут выполняться более одного раза одновременно. Таким образом, код PHP может мешать самому себе так же, как и код Java.

Сессии обычно не являются источником условий гонки. Каждый сеанс ограничивается одним клиентом с помощью файла cookie, который используется в качестве ключа, поэтому для анализа того, что может пойти не так, необходимо одновременно рассматривать только страницы и действия, предпринимаемые одним пользователем. Однако самая большая проблема заключается в устаревании данных, представленных в разных представлениях, пока они обновляются в модели, но в мире Ajax нужно искать обходной путь из-за природы протокола HTTP.

Базы данных , по крайней мере реляционные, в определенной степени гарантируют атомарность операций с определением транзакций . Базы данных — это квинтэссенция состояния приложения, и вы можете использовать их свойства ACID в транзакции. Например, если вам действительно нужно вычислить новый ключ для таблицы, взглянув на уже существующие:

SELECT MAX(id) + 1 FROM mytable;
INSERT INTO mytable VALUES (4242, 'The name', ...); -- 4242 is the new id
UPDATE mytable SET field = ... WHERE id = 4242;

по крайней мере, оберните транзакцию этим набором изменений, чтобы не завершить обновление какой-либо другой 4242-й строки, которая была вставлена ​​другим экземпляром сценария PHP после вашего SELECT. PDO генерирует исключения (в отличие от функций mysql _ * (), которые часто не работают бесшумно), поэтому этот пример должен работать в любом случае, когда происходит сбой INSERT. Но почему происходит сбой, когда нет ничего, что мешало бы вставить строку с правильным идентификатором? Это не ошибка пользователя. Вы можете увидеть потенциал для состязаний при выполнении интенсивных запросов в PHP-скриптах.

Файловая система не защищена от проблем параллелизма , а также; в этом случае строки заменяются файлами, а первичные ключи — именами файлов. Лучшие решения включают использование естественного ключа для имени файла или генерацию хеша:

$filename = uniqid('prefix', true); // second parameter is 'more cool?'

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

mt_srand();
$filename = uniqid(mt_rand(), true);

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