Конкурентность и многопоточность в PHP: от теории к практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Конкурентность и многопоточность в PHP: современные подходы

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

Что такое конкурентность и многопоточность?

Конкурентность — это способность программы выполнять несколько задач «одновременно», переключаясь между ними. Многопоточность — частный случай конкурентности, когда задачи выполняются в отдельных потоках операционной системы.

В PHP до версии 7.4 не было нативной поддержки потоков. Однако с появлением расширений pthreads и parallel, а также улучшением работы с процессами, ситуация изменилась.

1. Многопоточность с расширением parallel

Расширение parallel — современный способ работы с потоками в PHP 7.4+. Оно использует общую память, что позволяет эффективно обмениваться данными между потоками.

Установка:

pecl install parallel

Пример создания потока:

<?php
use parallel\Runtime;

$runtime = new Runtime();
$future = $runtime->run(function() {
    // Тяжелые вычисления
    $result = 0;
    for ($i = 0; $i < 1000000; $i++) {
        $result += sqrt($i);
    }
    return $result;
});

echo "Основной поток продолжает работу...\n";
$result = $future->value(); // Получаем результат
echo "Результат: $result\n";
?>

Преимущества parallel:

  • Нативная поддержка потоков без лишних зависимостей
  • Общая память между потоками
  • Возможность передачи замыканий и объектов

Недостатки:

  • Требует PHP 7.4+ и ZTS (Zend Thread Safety)
  • Не поддерживается на Windows
  • Сложности с отладкой

2. Асинхронность через процессы: pcntl_fork

Если вам не нужна общая память, можно использовать процессы с помощью pcntl_fork(). Это классический Unix-подход.

<?php
$pid = pcntl_fork();

if ($pid == -1) {
    die("Ошибка создания процесса");
} elseif ($pid) {
    // Родительский процесс
    echo "Родитель: ждем дочерний процесс...\n";
    pcntl_wait($status);
    echo "Родитель: дочерний процесс завершен\n";
} else {
    // Дочерний процесс
    echo "Дочерний: выполняю задачу...\n";
    sleep(3);
    exit(0);
}
?>

Особенности работы с процессами:

  • Каждый процесс имеет собственную память
  • Для обмена данными используйте сокеты, разделяемую память (shmop) или файлы
  • Необходимо обрабатывать зомби-процессы через pcntl_wait()

3. Конкурентность через асинхронные функции и ReactPHP

Для задач ввода-вывода (сетевые запросы, чтение файлов) эффективнее использовать асинхронные библиотеки, такие как ReactPHP или Amp. Они не создают потоки, но позволяют выполнять задачи конкурентно через event loop.

Пример с ReactPHP:

<?php
require 'vendor/autoload.php';

use React\EventLoop\Factory;
use React\Http\Browser;

$loop = Factory::create();
$client = new Browser($loop);

$promises = [];
$urls = [
    'https://api.example.com/data1',
    'https://api.example.com/data2',
    'https://api.example.com/data3'
];

foreach ($urls as $url) {
    $promises[] = $client->get($url);
}

// Ждем все запросы параллельно
$results = [];
foreach ($promises as $promise) {
    $results[] = $promise->wait();
}

$loop->run();
print_r($results);
?>

Когда использовать асинхронность:

  • Множество HTTP-запросов к внешним API
  • Обработка WebSocket-соединений
  • Работа с базами данных (неблокирующие драйверы)

4. Сравнение подходов и лучшие практики

Подход Производительность Сложность Использование памяти Подходит для
parallel Высокая Средняя Общая CPU-интенсивные задачи
pcntl_fork Средняя Высокая Изолированная Надежные процессы
ReactPHP Высокая для I/O Средняя Низкая Сетевые операции

Рекомендации:

  1. Используйте parallel для параллельных вычислений (обработка изображений, шифрование).
  2. Отдавайте предпочтение ReactPHP для задач, связанных с ожиданием (API, базы данных).
  3. Избегайте pcntl_fork в веб-приложениях — это сложно и небезопасно.
  4. Всегда обрабатывайте ошибки в дочерних потоках/процессах.
  5. Тестируйте под нагрузкой — многопоточность может привести к состоянию гонки.

Заключение

Конкурентность и многопоточность в PHP — не миф, а реальность, доступная каждому разработчику. Вы

Блоги

Рекомендаций по книгам