Многозадачность в PHP

Сложилось общее мнение, что из-за отсутствия в PHP потоков, как в C++ или JavaPHP не является многозадачным языком. Грубо говоря нельзя исполнять несколько команд одновременно. В результате многие разработчики выходили из положения с помощью эмуляции в виде асинхронных сокетов(Asynchronous Sockets) или мультикурл (curl_multi_init), некоторые использовали библиотеку pcntl_fork которая не входит в стандартную сборку PHP. Да, PHP не поддерживает обработку потоков, но он может быть многозадачным.

 

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

 

В PHP 5ой версии появились функции stream_select и proc_open, которые позволяют создавать и опрашивать потоки наподобие сокетов, но исполняя программу локально.

 

Надо сразу оговориться что для основной программы и потока понадобятся разные файлы.

 

 

 

<?php

 

$timeout=10; //раз во сколько секунд опрашивать потоки(при 0 мы будем опрашивать

//потоки постоянно, но этот режим потребляет много памяти

$streams=array();//массив потоков

$handles=array();//массив ссылок на потоки

$all_pipes=array();//массив настроек для каждого потока

$maxthreads=10;//количество потоков

 

 

for ($id=0; $id <= $maxthreads; $id++) {

$error_log="/tmp/error" . $id . ".log"//файл ошибок соответствует номеру потока

$descriptorspec=array(

0 => array("pipe", "r"),

1 => array("pipe", "w"),

2 => array("file", $error_log, "w")

);

$cmd='php ./thread.php '.$id;//команда которую вызывает поток,

//в данном случае мы вызываем исполнение скрипта потока

$handles[$id]=proc_open($cmd, $descriptorspec, $pipes);//создаем поток

$streams[$id]=$pipes[1];

$all_pipes[$id]=$pipes;

}

 

while (count($streams)) {//пока все потоки не исполнились

$read=$streams;

stream_select($read, $w=null, $e=null, $timeout);//опрашиваем потоки

foreach ($read as $r) {

$id=array_search($r, $streams);

echo stream_get_contents($all_pipes[$id][1]);//выводим на экран все что возращает скрипт

if (feof($r)) {//если поток выполнился возвращается EOF

//закрываем поток

fclose($all_pipes[$id][0]);

fclose($all_pipes[$id][1]);

$return_value=proc_close($handles[$id]);

unset($streams[$id]);//удаляем информацию о нем из массива

}

}

}

?>

 

 

Как вы видите мы не обращаемся к веб серверу, это позволяет экономить процессорное время, что соответственно увеличивает производительность.

 

Всегда ваш,

 

Александр 7ion.


© Alexander Semion