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

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

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

Начать курс

Введение в конкурентность и многопоточность в PHP

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

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

Основные концепции конкурентности и многопоточности

Синхронность vs Асинхронность

В синхронной модели PHP выполняет код строго последовательно. Если один скрипт делает HTTP-запрос, выполнение останавливается до получения ответа. Асинхронность позволяет запускать задачи, не дожидаясь их завершения.

Пример синхронного кода:

<?php
$result1 = file_get_contents('https://api.example.com/data1');
$result2 = file_get_contents('https://api.example.com/data2');
// Оба запроса выполняются последовательно
?>

Потоки и процессы

  • Процесс — изолированная копия программы со своим адресным пространством.
  • Поток — легковесная единица выполнения внутри процесса, разделяющая память.

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

Гонка данных и синхронизация

При работе нескольких потоков с общими данными может возникнуть состояние гонки (race condition). Для предотвращения используются мьютексы (mutex), блокировки и атомарные операции.

Инструменты для многопоточности в PHP

Расширение pthreads

pthreads — одно из первых расширений для работы с потоками. Оно предоставляет классы Thread, Worker и Pool. Важно: расширение работает только в CLI-режиме и требует ZTS (Zend Thread Safety) сборки PHP.

Пример простого потока:

<?php
class MyThread extends Thread {
    public function run() {
        echo "Поток запущен\n";
    }
}
$thread = new MyThread();
$thread->start();
$thread->join();
?>

Расширение parallel

parallel — более современное расширение, появившееся в PHP 7.4. Оно удобнее и безопаснее. Работает через каналы (channels) для передачи данных между потоками.

Пример с parallel:

<?php
$runtime = new \parallel\Runtime();
$future = $runtime->run(function(){
    return "Привет из параллельного потока!";
});
echo $future->value(); // Получаем результат
?>

Асинхронность через ReactPHP и Amp

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

  • ReactPHP — событийно-ориентированная библиотека с event loop.
  • Amp — async/await для PHP, похожий на JavaScript.

Практические примеры и лучшие практики

Пример: Параллельная обработка URL

Предположим, нужно проверить доступность 10 сайтов. Синхронно это займет много времени. Используем parallel для ускорения:

<?php
$urls = [
    'https://google.com',
    'https://yandex.ru',
    'https://github.com'
];

$runtimes = [];
$futures = [];

foreach ($urls as $url) {
    $runtime = new \parallel\Runtime();
    $runtimes[] = $runtime;
    $futures[] = $runtime->run(function($url) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return "$url — код $httpCode";
    }, [$url]);
}

foreach ($futures as $future) {
    echo $future->value() . "\n";
}
?>

Обработка ошибок и исключения

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

<?php
$runtime = new \parallel\Runtime();
$future = $runtime->run(function(){
    try {
        // опасный код
        return "Успех";
    } catch (\Throwable $e) {
        return "Ошибка: " . $e->getMessage();
    }
});
echo $future->value();
?>

Ограничения многопоточности в PHP

  • Расширения pthreads и parallel не работают на веб-серверах (только CLI).
  • Нельзя передавать между потоками ресурсы (например, соединения с БД).
  • Глобальные переменные не синхронизируются автоматически.

Заключение

Конкурентность и многопоточность в PHP — мощные инструменты, но они требуют осторожного подхода. Для большинства веб-задач достаточно асинхронных библиотек (ReactPHP, Amp). Если же вам нужна настоящая параллельная обработка (например, обработка больших массивов данных, парсинг, фоновые задачи), используйте parallel.

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

Блоги

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