Модель конкурентности PHP
Разберитесь в блокирующем вводе-выводе и узнайте, где помогает асинхронность
«Модель конкурентности PHP» — бесплатный урок PHP Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения PHP Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс PHP Academy содержит 4 уроков всего.
По умолчанию PHP работает синхронно
Классический PHP обрабатывает один запрос на процесс, последовательно, сверху вниз, с блокирующим вводом-выводом. Вызов file_get_contents() для медленного API замораживает весь сценарий, пока не поступят данные. Модель отсутствия общего состояния (новый процесс для каждого запроса, состояние уничтожается в конце) замечательно проста, но это означает, что один рабочий процесс может делать только одну вещь за раз. В этом уроке показано, где это создаёт проблемы и где помогает асинхронность.
Блокирующий ввод-вывод на практике
Обратите внимание: время по часам определяется ожиданием, а не вычислениями. Три последовательных запроса длительностью 1 секунду занимают примерно 3 секунды, хотя всё это время CPU простаивает.
<?php
$start = microtime(true);
foreach (['a', 'b', 'c'] as $job) {
usleep(1_000_000); // simulate a 1s blocking network call
echo "done $job\n";
}
printf("elapsed: %.2fs\n", microtime(true) - $start); // ~3.00sОграничение процессором и вводом-выводом
Параллельность помогает только при работе, ограниченной вводом-выводом, — когда время уходит на ожидание сети, диска или базы данных. При работе, ограниченной процессором (хеширование, изменение размера изображений, разбор данных), один поток уже полностью занят, а асинхронность лишь добавляет накладные расходы на планирование. Сначала определите причину:
- Много медленных вызовов HTTP или запросов к базе данных → асинхронность даёт большой выигрыш.
- Плотные числовые циклы → используйте параллельное выполнение (процессы или потоки), а не цикл обработки событий.
Как PHP масштабировался до сих пор
Исторически PHP масштабировался с помощью параллельности на уровне процессов: PHP-FPM поддерживает пул рабочих процессов, каждый из которых обрабатывает один запрос за раз, а OS распределяет между ними работу. Это настоящая параллельность между запросами, но внутри одного запроса она ничего не даёт: обычный блокирующий код по-прежнему не позволяет параллельно запустить 50 вызовов API внутри одного обработчика.
Идея цикла событий
Асинхронный PHP вводит цикл событий: один поток регистрирует операции ввода-вывода как неблокирующие, затем засыпает в stream_select()/epoll, пока какая-либо из них не будет готова, и вызывает обратные вызовы по мере поступления данных. Один поток управляет тысячами сокетов, потому что никогда не простаивает в ожидании одного-единственного сокета.
<?php
// Conceptual loop: poll many non-blocking streams at once
$streams = openManyNonBlockingSockets();
while ($streams) {
$read = $streams; $write = $except = [];
stream_select($read, $write, $except, null); // sleeps until ANY is ready
foreach ($read as $s) {
handleReadyStream($s); // runs only the sockets that have data now
}
}Кооперативность, а НЕ вытеснение
Асинхронный PHP работает кооперативно: задачи выполняются, пока добровольно не передадут управление в точке ввода-вывода. Вытеснения нет, поэтому длительный блокирующий вызов или плотный цикл вычислений останавливает весь цикл — все остальные задачи остаются без ресурсов. Золотое правило: никогда не вызывайте блокирующие функции (sleep, блокирующий PDO, file_get_contents) внутри задачи цикла событий. Используйте асинхронные эквиваленты цикла.
Три подхода в PHP
Современный PHP предлагает многоуровневый набор инструментов:
- Волокна (PHP 8.1, ядро) — низкоуровневый примитив для приостановки и возобновления стека вызовов. Это строительный блок, а не полноценная платформа.
- ReactPHP / Amp — циклы событий пользовательского уровня, построенные на потоках (а теперь и на волокнах), для неблокирующего ввода-вывода без расширений.
- Swoole / OpenSwoole — расширение на языке C, предоставляющее корутины, высокопроизводительный сервер и перехват блокирующих вызовов.
Перед выбором стратегии определяйте, что доступно во время выполнения.
<?php
echo 'Fibers: ' . (class_exists('Fiber') ? 'yes' : 'no'), PHP_EOL;
echo 'Swoole ext: ' . (extension_loaded('swoole') ? 'yes' : 'no'), PHP_EOL;
echo 'parallel ext: ' . (extension_loaded('parallel') ? 'yes' : 'no'), PHP_EOL;
echo 'PHP ' . PHP_VERSION, PHP_EOL;Обещания моделируют будущие значения
До появления волокон асинхронный PHP представлял ожидающие результаты как обещания: заполнители для значений, которые будут разрешены позже. К ним присоединяют обратные вызовы через then(). Это работает, но приводит к вложенным цепочкам; волокна позволяют библиотекам скрыть их за синхронно выглядящим await.
<?php
use React\Promise\Promise;
$promise = new Promise(function ($resolve) {
// resolved later when I/O completes
$resolve(42);
});
$promise->then(function ($value) {
echo "got $value\n";
});Конкурентность и параллелизм
Будьте точны:
- Конкурентность — множество задач выполняются, перемежаясь в одном потоке (цикле событий). Отлично подходит для ввода-вывода.
- Параллелизм — множество задач выполняются одновременно на нескольких ядрах (процессы, расширение
parallel, рабочие процессы Swoole). Нужен для вычислений.
Цикл событий обеспечивает конкурентность, но не параллелизм. Фрагмент ниже показывает параллелизм на уровне процессов с помощью proc_open — три рабочих процесса одновременно выполняются в отдельных процессах ОС.
<?php
$procs = [];
foreach (range(1, 3) as $i) {
// Each worker is a separate OS process -> real parallelism
$procs[] = proc_open(
"php -r 'usleep(500000); echo \"worker $i done\\n\";'",
[1 => ['pipe', 'w']], $pipes
);
}
foreach ($procs as $p) { proc_close($p); }
echo "all workers launched in parallel\n";Когда НЕ следует выбирать асинхронность
У асинхронности есть издержки: долгоживущий рабочий процесс означает, что PHP больше не очищает состояние между запросами, поэтому утечки памяти, статические кэши и устаревшие соединения становятся вашей заботой. Если обработчик запроса выполняет один запрос к базе данных и возвращает результат, PHP-FPM проще и столь же быстр. Выбирайте асинхронность, когда требуется массовый параллельный ввод-вывод, WebSockets/длительный опрос, потоковая передача данных или постоянно работающий сервер.
Настоящий параллелизм: расширение parallel и процессы
Для распределения вычислительной нагрузки PHP может использовать настоящий параллелизм на уровне ОС. Расширение parallel запускает рабочие потоки с изолированной памятью; в качестве альтернативы proc_open/pcntl_fork создают процессы. В отличие от цикла событий, эти средства одновременно используют несколько ядер — это правильный выбор, когда узкое место находится в вычислениях, а не в ожидании.
<?php
// Requires ext-parallel — runs tasks on separate threads/cores
use parallel\Runtime;
$runtime = new Runtime();
$future = $runtime->run(function (): int {
$sum = 0;
for ($i = 0; $i < 1_000_000; $i++) $sum += $i; // CPU work on another core
return $sum;
});
echo $future->value(), PHP_EOL;Быстрая проверка
Что произойдёт, если вызвать блокирующий sleep(2) внутри задачи цикла событий?
Итоги
Теперь у вас есть мысленная модель конкурентности в PHP:
- PHP по умолчанию работает синхронно, блокирующе и без общего состояния; FPM масштабируется между запросами, но не внутри одного запроса.
- Асинхронность помогает выполнять операции, ограниченные вводом-выводом, через однопоточный цикл событий с использованием неблокирующих потоков.
- Модель является кооперативной — никогда не блокируйте цикл; длительные вычисления требуют параллелизма, а не конкурентности.
- Инструменты PHP: волокна (примитив), ReactPHP/Amp (циклы пользовательского уровня и обещания), Swoole (расширение для корутин).
Часто задаваемые вопросы
Урок «Модель конкурентности PHP» бесплатный?
Да — полный текст урока «Модель конкурентности PHP» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс PHP Academy, подпишись на CoddyKit PRO. Курс PHP Academy содержит 4 уроков всего.
Чему я научусь в уроке «Модель конкурентности PHP»?
Разберитесь в блокирующем вводе-выводе и узнайте, где помогает асинхронность Ты практикуешь PHP Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать PHP Academy?
Предыдущий опыт не требуется. PHP Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Модель конкурентности PHP»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке PHP Academy?
Да. Каждый урок PHP Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Модель конкурентности PHP
- Волокна PHP 8.1
- Циклы событий с ReactPHP
- Высокопроизводительные серверы со Swoole