0Pricing
PHP Academy · Урок

Модель конкурентности 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 — локальная установка не требуется.

Все уроки этого курса

  1. Модель конкурентности PHP
  2. Волокна PHP 8.1
  3. Циклы событий с ReactPHP
  4. Высокопроизводительные серверы со Swoole
← Назад к PHP Academy