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

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

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

Начать курс

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

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

1. Основы многопоточности в PHP: расширение pthreads

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

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

<?php
class WorkerThread extends Thread {
    private $id;
    
    public function __construct($id) {
        $this->id = $id;
    }
    
    public function run() {
        echo "Поток {$this->id} запущен\n";
        sleep(2);
        echo "Поток {$this->id} завершен\n";
    }
}

$threads = [];
for ($i = 1; $i <= 5; $i++) {
    $threads[$i] = new WorkerThread($i);
    $threads[$i]->start();
}

foreach ($threads as $thread) {
    $thread->join();
}
?>

Метод start() запускает поток, а join() ожидает его завершения. Без join() основной поток может завершиться раньше дочерних.

2. Синхронизация потоков и разделяемые ресурсы

При работе с многопоточностью критически важно синхронизировать доступ к общим данным. PHP предоставляет несколько механизмов: Mutex, Volatile и Threaded объекты.

Mutex (взаимное исключение) — блокирует доступ к ресурсу для других потоков, пока один поток работает с ним.

<?php
class Counter extends Threaded {
    private $count = 0;
    
    public function increment() {
        $this->synchronized(function() {
            $this->count++;
        });
    }
    
    public function getCount() {
        return $this->count;
    }
}

class IncrementThread extends Thread {
    private $counter;
    
    public function __construct(Counter $counter) {
        $this->counter = $counter;
    }
    
    public function run() {
        for ($i = 0; $i < 1000; $i++) {
            $this->counter->increment();
        }
    }
}

$counter = new Counter();
$threads = [];

for ($i = 0; $i < 10; $i++) {
    $threads[$i] = new IncrementThread($counter);
    $threads[$i]->start();
}

foreach ($threads as $thread) {
    $thread->join();
}

echo "Итоговое значение: " . $counter->getCount(); // 10000
?>

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

$sharedFlag = new Volatile();
$sharedFlag->flag = false;

// В одном потоке
$sharedFlag->flag = true;

// В другом потоке
if ($sharedFlag->flag) {
    echo "Флаг установлен";
}

3. Параллельные процессы и асинхронные задачи

Если расширение pthreads недоступно (например, на общем хостинге), можно использовать параллельные процессы через pcntl_fork() или асинхронные библиотеки, такие как ReactPHP или Amp.

pcntl_fork() — создает дочерний процесс, который выполняется параллельно с родительским.

<?php
$pid = pcntl_fork();
if ($pid == -1) {
    die("Не удалось создать процесс");
} elseif ($pid) {
    // Родительский процесс
    echo "Родительский процесс, PID дочернего: $pid\n";
    pcntl_wait($status); // Ожидание завершения дочернего процесса
} else {
    // Дочерний процесс
    echo "Дочерний процесс, PID: " . getmypid() . "\n";
    sleep(2);
    echo "Дочерний процесс завершен\n";
    exit(0);
}
?>

ReactPHP — библиотека для асинхронного программирования. Пример асинхронного HTTP-запроса:

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

use React\EventLoop\Factory;
use React\HttpClient\Client;

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

$request = $client->request('GET', 'https://api.example.com/data');
$request->on('response', function ($response) {
    $response->on('data', function ($chunk) {
        echo "Получен чанк: " . strlen($chunk) . " байт\n";
    });
});

$loop->run();
?>

Асинхронные библиотеки позволяют выполнять множество операций (сетевые запросы, чтение файлов) без блокировки основного потока.

4. Управление пулом потоков и очередями

Для эффективного управления ресурсами часто используется пул потоков (Thread Pool). Это позволяет ограничить количество одновременно работающих потоков и переиспользовать их.

Реализация простого пула потоков на основе pthreads:

<?php
class Worker extends Thread {
    private $pool;
    
    public function __construct($pool) {
        $this->pool = $pool;
    }
    
    public function run() {
        while (true) {
            $task = $this->pool->getTask();
            if ($task === null) break;
            $task->execute();
        }
    }
}

class Task extends Threaded {
    private $data;
    
    public function __construct($data) {
        $this->data = $data;
    }
    
    public function execute() {
        echo "Обработка: " . $this->data . "\n";
        sleep(1);
    }
}

class ThreadPool {
    private $workers = [];
    private $queue = [];
    
    public function __construct($numWorkers) {
        for ($i = 0; $i < $numWorkers; $i                

Блоги

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