PDA

Просмотр полной версии : многопоточность в php


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

ну в общем если есть у когонить инфа желательно с примером - если будет ;)

Stefun
27.10.2007, 23:18
Первый раз слышу что в пхп можно сделать многопоточность, мда...
В пхп нельзя это реализовать

inv
27.10.2007, 23:36
точно не знаю....
в любом случае два варианта
если ты работаешь с сокетами
1.асинхронные сокеты (то что ты и сказал - перебирать); curl_multi_init();
2.использовать библиотеки там есть многопроцессорность (никакой информации у меня нет,могу ошибаться)

***
pcntl_fork()

EST a1ien
27.10.2007, 23:36
Думаю что кроме того сто ты привелтам одно извращенство типо создания нескольких сокетов и последовательного чтения из каждого и тд
Врядли чтото прокатит

KSURi
27.10.2007, 23:38
пора бы уже знать, что НЕТ

genom--
27.10.2007, 23:42
я знаю что нет -- но тем не менее мб ченить кроме этих сокетов -- придумали уже

KSURi
28.10.2007, 02:06
Пул неблокирующих сокетов - это не многопоточность
Ничего не придумали и врядли придумают

Isis
28.10.2007, 02:51
перебирать != многопоточность

DIAgen
28.10.2007, 11:39
Много порочность врятли получиться но есть два выхода что можно сделать и оба под php5...
1) Это использовать url_multi_exec (http://ru2.php.net/curl_multi_exec)
<?php
// create both cURL resources
$ch1 = curl_init();
$ch2 = curl_init();

// set URL and other appropriate options
curl_setopt($ch1, CURLOPT_URL, "http://www.google.com/");
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_URL, "http://www.php.net/");
curl_setopt($ch2, CURLOPT_HEADER, 0);

//create the multiple cURL handle
$mh = curl_multi_init();

//add the two handles
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);

$running=null;
//execute the handles
do {
curl_multi_exec($mh,$running);
} while ($running > 0);
//close the handles
curl_multi_remove_handle($mh,$ch1);
curl_multi_remove_handle($mh,$ch1);
curl_multi_close($mh);
?>
2) Рассмотри вот этот скрипт
<?php
if (!defined("MD_DEBUG")) define("MD_DEBUG", false);
function get_chanked($stream) {
$content = "";
do
{
$len = trim(fgets($stream, 10));
if (MD_DEBUG) echo "Find lable 0x$len<br>\n";
if ($len === false || !preg_match("|[0-9a-f]+|i", $len))
{
if (MD_DEBUG) echo "Failure getting content<br>\n";
return "";
}
else
{
$len = hexdec($len);
if (MD_DEBUG) echo "Reading $len byte<br>\n";
}
if ($len > 0)
{
$readed = 0;
while($readed < $len + 2)
{
$buffer = fgets($stream);
$readed += strlen($buffer);
$content .= $buffer;
}
}
} while($len > 0);
return $content;
}
function MyDownload ($urls, $timeout = 30) {
$streams = array();
$result = array();
foreach($urls as $url)
{
$host = parse_url($url, PHP_URL_HOST);
if ($host === null)
{
if (MD_DEBUG) echo "Host in '$url' is not correct<br>\n";
continue;
}
$port = parse_url($url, PHP_URL_PORT);
$stream = @stream_socket_client(// Если не удается законнектится то выводится Warning, а он нам тут совершенно не нужен
"tcp://$host:".($port === null ? "80" : $port),
$errno, $errstr, $timeout,
STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT) ; // Асинхронное соединение, что бы не ждать коннекта...
if ($errno > 0)
{
if (MD_DEBUG) echo "Error connectiong to $url. $errstr($errno)<br>\n";
// либо еще что-нибудь делаем. Пишем в логи итп...
continue;
}
stream_set_blocking($stream, 0); // Для всех потоков ставим режим non-blocking, что бы не ждать когда данные будут доступны для чтения/записи
$streams[$url] = $stream;
}
$toException = $toRead = $toWrite = $streams; // Два массива потоков, для чтения и для записи. Для чтения можно сразуже задавать, т.к. все равно они не будут доступны пока не будет послан запрос
do
{
$rStreams = $toRead;
$wStreams = $toWrite;
$eStreams = $toException;
$num = @stream_select($rStreams, $wStreams, $eStreams, 0); // Выбираем какой-нибудь поток. Последний параметр 0 - не ждать таймаута

if ($num > 0) // Если $num == 0, то можно добавить в общий массив новых потоков, что бы не stream_select не простаивал, но я не стал этого делать
{
if (count($wStreams) > 0)
{
foreach($wStreams as $write)
{
$url = array_search($write, $streams);
$host = parse_url($url, PHP_URL_HOST);
$request = "GET $url HTTP/1.1\r\nAccept: */*\r\nnAccept-Language: en-us\r\nPragma: no-cache\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0)\r\nHost: $host\r\nConnection: Close\r\n\r\n";
if (MD_DEBUG) echo "Sending request for $url<br>\n$request";
fwrite($write, $request);
unset($toWrite[array_search($write, $toWrite)]); // Запрос послан, больше не надо выбирать поток для записи
}
}
if (count($rStreams) > 0)
{
foreach($rStreams as $read)
{
$url = array_search($read, $streams);
$key = array_keys ($urls, $url); $key = $key[0];
if (MD_DEBUG) echo "Getting data for $url<br>\n";
$isChunk = false;
$charset = "";
$result[$key][0] = "";
while(($header = fgets($read)) != "\r\n") {
if (!(strpos($header, "Content-Type")===false)) if (!(strpos($header, "charset=")===false)) $charset = substr($header, strpos($header, "charset=")+8);
else $charset = "default";
if (preg_match("|Transfer-Encoding:\s+chunked|i", $header)) $isChunk = true;
}
if ($isChunk) $result[$key][0] = get_chanked($read);
else while (!feof($read)) $result[$key][0] .= fread($read, 1024);
$result[$key][1] = $charset;
unset($toRead[array_search($read, $toRead)]); // Ответ получен, поток не нужен больше
}
}
if (count($eStreams) > 0)
{
foreach($eStreams as $exception)
{
$url = array_search($exception, $streams);
if (MD_DEBUG) echo "Fail getting data for $url.<br>\n";
// Здесь так же кричим, что сервер вернул не то что ожидали и выкидываем этот поток из стека
}
unset($toRead[array_search($exception, $toRead)]);
unset($toWrite[array_search($exception, $toWrite)]);
unset($toException[array_search($exception, $toException)]);
}
}
} while(count($toRead) > 0); // Читать нечего больше
return $result;
}
$urls = array(
"yandex"=>"http://www.yandex.ru/yandsearch?text=Превед",
"whois"=>"http://www.service-whois.ru/?domain=mail.ru"
);
$sites = MyDownload ($urls);
$yandex1 = $sites["yandex"][0];
$yandex2 = $sites["yandex"][1];
?>
Даный скрипт будет работать только в php5, не много поточность... но работает быстро и эффективно...

genom--
28.10.2007, 11:47
лан спс всем -- будем думать

DIAgen
28.10.2007, 11:48
Второй пример можно переделать под php 4, заменив работу функции stream_socket_client на fsockopen или Socket


P.S. Вот уже есть готовый класс для работы с потоками http://multi-downoader.googlecode.com/svn/trunk/Downloader/

ZaCo
28.10.2007, 12:23
тема обсуждалась http://forum.antichat.ru/showthread.php?p=200343
и еще, СТАНДАРТНЫХ средств в пхп для многопоточности нет. но если вам необходимо качественное (все приведенные методы медленные и в общем случае нерабочие) решение, то теоретически можно написать свой модуль.
а запускать функции через это http://man.chinaunix.net/develop/php/php_manual_zh/html/zend.calling-user-functions.html
к сожалению как устроены переменные в пхп не знаю, поэтому утвержадть о работоспособности такого способа не могу.

_Great_
28.10.2007, 12:32
перебирать != многопоточность
ядро ОС перебирает готовые потоки, выделяя каждому квант времени. утверждение неверно =)))

( расценивать как шутку )

groundhog
28.10.2007, 14:11
Многопоточность в PHP есть! Почему всё время забывают про pcntl_fork? Другое дело, что такое возможно реализовать только на Unix платформе, и если PHP включён не модулем, а как CGI.

fucker"ok
28.10.2007, 14:49
>Почему всё время забывают про pcntl_fork?
Потому-что в среднем php процесс занимает 16мб памяти и можно посчитать на сколько хватит памяти, если форкать много раз :)

ZaCo
28.10.2007, 14:56
2groundhog
>>СТАНДАРТНЫХ средств в пхп для многопоточности нет
и еще, поток это единица процесса, адресное пространство, за исключением стека и регистров, что для php не надо, одно и это в общем-то выгодное для определенных задач отличие от двух родственных процессов, поэтому если ищут именно решение для организации многопоточной программы не нужно давать решение для другой задачи.

groundhog
28.10.2007, 15:06
и еще, поток это единица процесса, адресное пространство, за исключением стека и регистров, что для php не надо, одно и это в общем-то выгодное для определенных задач отличие от двух родственных процессов, поэтому если ищут именно решение для организации многопоточной программы не нужно давать решение для другой задачи.
Бред какой-то...

Потому-что в среднем php процесс занимает 16мб памяти и можно посчитать на сколько хватит памяти, если форкать много раз
Это вопрос конфигурации. ТС не ставил определённой задачи, а искал метод реализации - я его и предоставил.

_Great_
28.10.2007, 15:17
>Почему всё время забывают про pcntl_fork?
Потому-что в среднем php процесс занимает 16мб памяти и можно посчитать на сколько хватит памяти, если форкать много раз :)
вроде как в линуксе потоки реализованы как процессы.

nerezus
28.10.2007, 15:41
Многопоточность в PHP есть! Почему всё время забывают про pcntl_fork? Это не потоки. Учи матчасть.

Многопоточности в PHP нету и не будет. Тема закрыта.

_Great_
28.10.2007, 17:25
Это не потоки. Учи матчасть.

Многопоточности в PHP нету и не будет. Тема закрыта.

нер, про матчасть стоит почитать имхо тебе.

http://en.wikipedia.org/wiki/Light-weight_process
читай это и все ссылки из See Also