Иногда появляется необходимость выполнять несколько действий одновременно, например, проверять изменения в одной таблице БД и вносить модификации в другую. Причем если одна из операций (например, проверка изменений), занимает много времени, очевидно, что последовательное выполнение не обеспечит балансировки ресурсов.
Для решения такого рода задач, в программировании используется многопоточность - каждая операция помещается в отдельный поток с выделенным объемом ресурсов и работает внутри него. При таком подходе, все задачи будут выполнятся отдельно и независимо.
Хотя 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 - мне будет приятно.