Подписка

Многопоточность в PHP

Иногда появляется необходимость выполнять несколько действий одновременно, например, проверять изменения в одной таблице БД и вносить модификации в другую. Причем если одна из операций (например, проверка изменений), занимает много времени, очевидно, что последовательное выполнение не обеспечит балансировки ресурсов.

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

Хотя PHP и не поддерживает многопоточность, есть несколько методов её эмуляции, о них и пойдет речь ниже.

1. Запуск нескольких копий скрипта - по копии для операции

//woman.php
if (!isset($_GET['thread'])) {
    system('wget http://localhost/woman.php?thread=make_me_happy');
    system('wget http://localhost/woman.php?thread=make_me_rich');
} elseif ($_GET['thread'] == 'make_me_happy') {
    make_her_happy();
} elseif ($_GET['thread'] == 'make_me_rich') {
    find_another_one();
}

Когда мы выполняем этот скрипт без параметров, он автоматически запускает две копии себя, с идентификаторами операций ('thread=make_me_happy' и 'thread=make_me_rich'), которые инициируют выполнение необходимых функций.

Таким образом мы достигаем нужного результата - две операции выполняются одновременно - но это конечно же не многопоточность, а просто костыль для выполнения задач одновременно.

2. Путь джедая - использование расширения PCNTL

PCNTL - расширение, позволяющее полноценно работать с процессами. Кроме управления, поддерживает отправку сообщений, проверку состояния и установку приоритетов. Вот так выглядит предыдущий скрипт с использованием PCNTL:

$pid = pcntl_fork();
if ($pid == 0) {
    make_her_happy();
} elseif ($pid > 0) {
    $pid2 = pcntl_fork();
    if ($pid2 == 0) {
       find_another_one();
    } 
}

Выглядит довольно запутанно, пройдемся построчно.

В первой строке мы "форкаем" текущий процесс (форк - копирование процесса из сохранением значений всех переменных), разделяя на два процесса (текущий и дочерний) выполняющихся параллельно.

Чтобы понять, где мы находимся в данный момент, в дочернем или материнском процессе, функция pcntl_fork возвращает 0 для дочернего и идентификатор процесса для материнского. Поэтому, во второй строке, мы смотрим на $pid, если он равен нулю, значит мы в дочернем процессе - выполняем функцию, в противном случае, мы находимся в материнском (строка 4), тогда создаем еще один процесс и аналогично выполняем задачу.

Процесс выполнения скрипта:

Таким образом скрипт создает еще 2 дочерних процесса, которые являются его копиями, содержат те же переменные с аналогичными значениями. А при помощи идентификатора возвращаемого функцией pcntl_fork мы ориентируемся в каком потоке находимся в данный момент и выполняем необходимые действия.

При использовании Pcntl, получаем полноценную работу с процессами, включая отправку/обработку сигналов, установку приоритетов и др. А для упрощения работы существует ряд библиотек, берущих на себя сложность реализации и предоставляющих простой интерфейс.

Продолжение: PCNTL - параллельные вычисления

В тему:

Если пост понравился - нажмите на +1 - мне будет приятно.

@kkooler

@kkooler

Занимаюсь разработкой высоконагруженных проектов и распределенных систем на PHP.
В свободное время разрабатываю нано-проекты:

Следить за блогом

RSS канал Twitter