Существует ли реалистичный способ реализации многопоточной модели в PHP, действительно ли это или просто имитировать ее. Некоторое время назад было высказано предположение, что вы можете заставить операционную систему загружать другой экземпляр исполняемого файла PHP и обрабатывать другие одновременные процессы.

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

Многопоточность возможна в php

Да, вы можете делать многопоточность в PHP с помощью pthreads

Из документации PHP:

pthreads – это объектно-ориентированный API, который предоставляет все инструменты, необходимые для многопоточности в PHP. Приложения PHP могут создавать, читать, писать, исполнять и синхронизировать с Threads, Workers и Threaded.

Предупреждение . Расширение pthreads нельзя использовать в среде веб-сервера. Поэтому потоки в PHP должны оставаться в приложениях на базе CLI.

Простой тест

#!/usr/bin/php arg = $arg; } public function run() { if ($this->arg) { $sleep = mt_rand(1, 10); printf("%s: %s -start -sleeps %d" . "\n", date("g:i:sa"), $this->arg, $sleep); sleep($sleep); printf("%s: %s -finish" . "\n", date("g:i:sa"), $this->arg); } } } // Create a array $stack = array(); //Initiate Multiple Thread foreach (range("A", "D") as $i) { $stack = new AsyncOperation($i); } // Start The Threads foreach ($stack as $t) { $t->start(); } ?>

Первый забег

12:00:06pm: A -start -sleeps 5 12:00:06pm: B -start -sleeps 3 12:00:06pm: C -start -sleeps 10 12:00:06pm: D -start -sleeps 2 12:00:08pm: D -finish 12:00:09pm: B -finish 12:00:11pm: A -finish 12:00:16pm: C -finish

Второй прогон

12:01:36pm: A -start -sleeps 6 12:01:36pm: B -start -sleeps 1 12:01:36pm: C -start -sleeps 2 12:01:36pm: D -start -sleeps 1 12:01:37pm: B -finish 12:01:37pm: D -finish 12:01:38pm: C -finish 12:01:42pm: A -finish

Пример реального мира

Error_reporting(E_ALL); class AsyncWebRequest extends Thread { public $url; public $data; public function __construct($url) { $this->url = $url; } public function run() { if (($url = $this->url)) { /* * If a large amount of data is being requested, you might want to * fsockopen and read using usleep in between reads */ $this->data = file_get_contents($url); } else printf("Thread #%lu was not provided a URL\n", $this->getThreadId()); } } $t = microtime(true); $g = new AsyncWebRequest(sprintf("http://www.google.com/?q=%s", rand() * 10)); /* starting synchronization */ if ($g->start()) { printf("Request took %f seconds to start ", microtime(true) - $t); while ($g->isRunning()) { echo "."; usleep(100); } if ($g->join()) { printf(" and %f seconds to finish receiving %d bytes\n", microtime(true) - $t, strlen($g->data)); } else printf(" and %f seconds to finish, request failed\n", microtime(true) - $t); }

почему вы не используете popen ?

For ($i=0; $i<10; $i++) { // open ten processes for ($j=0; $j<10; $j++) { $pipe[$j] = popen("script2.php", "w"); } // wait for them to finish for ($j=0; $j<10; ++$j) { pclose($pipe[$j]); } }

Threading недоступен на складе PHP, но одновременное программирование возможно с использованием HTTP-запросов в виде асинхронных вызовов.

Если параметр таймаута завитка установлен равным 1 и использует тот же session_id для процессов, которые вы хотите связать друг с другом, вы можете связываться с переменными сеанса, как в моем примере ниже. С помощью этого метода вы даже можете закрыть свой браузер, и одновременный процесс все еще существует на сервере.

Не забудьте проверить правильный идентификатор сеанса, например:

http: //localhost/test/verifysession.php? sessionid = [ правильный идентификатор]

startprocess.php

$request = "http://localhost/test/process1.php?sessionid=".$_REQUEST["PHPSESSID"]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $request); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 1); curl_exec($ch); curl_close($ch); echo $_REQUEST["PHPSESSID"];

process1.php

set_time_limit(0); if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); function checkclose() { global $_SESSION; if ($_SESSION["closesession"]) { unset($_SESSION["closesession"]); die(); } } while(!$close) { session_start(); $_SESSION["test"] = rand(); checkclose(); session_write_close(); sleep(5); } с set_time_limit(0); if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); function checkclose() { global $_SESSION; if ($_SESSION["closesession"]) { unset($_SESSION["closesession"]); die(); } } while(!$close) { session_start(); $_SESSION["test"] = rand(); checkclose(); session_write_close(); sleep(5); } в set_time_limit(0); if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); function checkclose() { global $_SESSION; if ($_SESSION["closesession"]) { unset($_SESSION["closesession"]); die(); } } while(!$close) { session_start(); $_SESSION["test"] = rand(); checkclose(); session_write_close(); sleep(5); }

verifysession.php

if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); session_start(); var_dump($_SESSION);

closeprocess.php

if ($_REQUEST["sessionid"]) session_id($_REQUEST["sessionid"]); session_start(); $_SESSION["closesession"] = true; var_dump($_SESSION);

В то время как вы не можете нить, у вас есть определенная степень управления процессом в php. Вот два полезных набора:

Функции управления процессом http://www.php.net/manual/en/ref.pcntl.php

Вы можете разветвить свой процесс с помощью pcntl_fork – вернуть PID дочернего элемента. Затем вы можете использовать posix_kill для использования этого PID.

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

Вы можете имитировать потоки. PHP может запускать фоновые процессы через popen (или proc_open). Эти процессы могут быть переданы с помощью stdin и stdout. Конечно, эти процессы могут быть программой php. Это, вероятно, так близко, как вы доберетесь.

Вы можете использовать exec () для запуска сценария командной строки (например, командной строки php), и если вы подключите вывод к файлу, ваш скрипт не будет ждать завершения команды.

Я не могу вспомнить синтаксис CLI php, но вам нужно что-то вроде:

Exec("/path/to/php -f "/path/to/file.php" | "/path/to/output.txt"");

Я думаю, что для нескольких общих серверов хостинга по умолчанию отключен exec () по соображениям безопасности, но, возможно, стоит попробовать.

В зависимости от того, что вы пытаетесь сделать, вы также можете использовать curl_multi для его достижения.

Он поддерживает двунаправленную межпоточную связь, а также имеет встроенную защиту для уничтожения дочерних потоков (предотвращение сирот).

У вас может быть опция:

  1. multi_curl
  2. Можно использовать системную команду для того же
  3. Идеальный сценарий – создание функции потоковой передачи на языке C и компиляция / настройка в PHP. Теперь эта функция будет функцией PHP.

Как насчет pcntl_fork?

проверьте нашу страницу руководства на примерах: PHP pcntl_fork

pcntl_fork не будет работать в среде веб-сервера, если включен безопасный режим . В этом случае он будет работать только в версии CLI для PHP.

Класс Thread доступен начиная с PECL pthreads ≥ 2.0.0.

В программировании постоянно приходиться работать с различными ресурсами: файлами, сокетами, http-соединениями . И у них у всех есть некий интерфейс доступа, часто несовместимый друг с другом. Поэтому, чтобы устранить данные несоответствия и унифицировать работу с различными источниками данных, начиная с PHP 4.3 были придуманы PHP Streams - потоки .

Несмотря на то, что PHP 4.3 вышел давным-давно, многие PHP-программисты имеют весьма отдаленное представление о потоках в PHP , и продолжают использовать CURL везде, хотя в PHP для этого существует более удобная альтернатива в виде контекста потоков (Stream Context) .

Следующие виды потоков существуют в PHP :

  • Файл на жестком диске;
  • HTTP-соединение с веб-сайтом;
  • Соединение UDP с сервером;
  • ZIP-файл ;
  • Файл *.mp3 .

Что общего есть во всех этих ресурсах? Все они могут быть прочитаны и записаны, т.е. к ним ко всем могут быть применены операции чтения и записи. Сила потоков PHP как раз и заключается в том, что вы можете получить доступ ко всем этим ресурсам, используя один и тот же набор функций. Это очень удобно. Также, если вдруг возникнет такая необходимость, Вы можете написать свою собственную реализацию обработчика потоков "stream wrapper" . Помимо чтения и записи, потоки в PHP также позволяет выполнять другие операции, такие как переименование и удаление.

Программируя на PHP , Вы уже встречались с потоками, хотя возможно не догадывались об этом. Так, функции, работающие с потоками - это fopen() , file_get_contents() , file() и т.д. Поэтому, фактически, Вы уже используете файловые потоки все это время, полностью прозрачно.

Для работы с другим типом потока, необходимо указать его протокол (wrapper) следующим образом: wrapper://some_stream_resource , где wrapper:// - это, например http:// , file:// , ftp:// , zip:// и т.д., а some_stream_resource - URI-адрес , идентифицирует то, что вы хотите открыть. URI-адрес не накладывает каких-либо ограничений на формат. Примеры:

  • http://сайт/php-stream-introduction.html
  • file://C:/Projects/rostov-on-don.jpg
  • ftp://user:[email protected]/pub/file.txt
  • mpeg://file:///music/song.mp3
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=

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

// список зарегистрированных транспортов сокета
print_r(stream_get_transports());

// список зарегистрированных потоков (обработчиков)
print_r(stream_get_wrappers());

// список зарегистрированных фильтров
print_r(stream_get_filters();

Контексты потоков PHP

Часто возникает необходимость указания дополнительных параметров при http-запросе. Контексты потоков решают эту проблему, позволяя указать дополнительные параметры. У многих функций, поддерживающих работу с потоками, есть необязательный параметр контекста потока. Давайте посмотрим на функцию file_get_contents() :

String file_get_contents(string $filename [, int $flags = 0 [, resource $context [, int $offset = -1 [, int $maxlen = -1]]]])

Как видно, третьим параметром передается контекст потока. Контексты создаются с помощью функции stream_context_create() , которая принимает массив и возвращает ресурс контекста.

$options = array(
"http" => array(
"method" => "GET",
"header" => "Accept-language: en\r\n".
"Cookie: foo = bar\r\n"
);

$context = stream_context_create($options);

// Используя это с file_get_contents ...
echo file_get_contents ("http://www.example.com/", 0, $context);

Таким образом, сегодня мы узнали, что такое потоки и контексты потоков в PHP , рассмотрели примеры их использования, а в следующих статьях мы поговорим о метаданных потока и создадим свой собственный обработчик.

  • Программирование ,
  • Параллельное программирование
  • Недавно я попробовал pthreads и был приятно удивлен - это расширение, которое добавляет в PHP возможность работать с несколькими самыми настоящими потоками. Никакой эмуляции, никакой магии, никаких фейков - все по-настоящему.



    Я рассматриваю такую задачу. Есть пул заданий, которые надо побыстрее выполнить. В PHP есть и другие инструменты для решения этой задачи, тут они не упоминаются, статья именно про pthreads.



    Что такое pthreads

    Вот и все! Ну почти все. На самом деле есть то, что может огорчить пытливого читателя. Все это не работает на стандартном PHP, скомпилированным с опциями по умолчанию. Чтобы насладиться многопоточностью, надо, чтобы в вашем PHP был включен ZTS (Zend Thread Safety).

    Настройка PHP

    Далее, PHP с ZTS. Не обращайте внимание на такую большую разницу во времени выполнения в сравнении с PHP без ZTS (37.65 против 265.05 секунд), я не пытался привести к общему знаменателю настройки PHP. В случае без ZTS у меня включен XDebug например.


    Как видно, при использовании 2-х потоков скорость выполнения программы примерно в 1.5 раза выше, чем в случае с линейным кодом. При использовании 4-х потоков - в 3 раза.


    Можно обратить внимание, что хоть процессор и 8-ядерный, время выполнения программы почти не менялось, если использовалось более 4 потоков. Похоже, это связано с тем, что физических ядра у моего процессора 4. Для наглядности изобразил табличку в виде диаграммы.


    Резюме

    В PHP возможна вполне элегантная работа с многопоточностью с использованием расширения pthreads. Это дает ощутимый прирост производительности.

    Теги:

    • php
    • pthreads
    Добавить метки

    Оффициальной нет, сразу скажу. Есть только многочисленные костылики. Примеры, в основном, будут полезны в сео софте.

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

    Собсно, теория реализации.

    Два скрипта — один поток, второй запускалка потоков. Поток делает свои дела, запускалка вычисляет, сколько потоков запустить и с какими параметрами.

    Пример первый, запускает 20 потоков, если общее количество потоков ниже чем 10

    1): $thCount = exec("ps a | thread.php | wc -l"); echo $thCount."\r\n"; if($thCount < 10){ for($i = 0; $i < 20; $i++){ echo "launch thread\r\n"; passthru("(php -f thread.php &) >> /dev/null 2>&1"); //sleep(1); } } else { sleep(5); } endwhile; ?>

    Получается хрень, которая дозапускает потоки время от времени по условию. Потоков всегда разное число в разные промежутки времени и остановить их никак нельзя. Но, если некритичны все эти вещи, цикл вполне неплохо работает. Подходит, например, всякие регеры. При допилке можно держать нужное количество потоков с погрешностью +- 1. Для этого нужно просто динамически вычислять число запускаемых потоков. Да, и ps показывает три работающих потока при таком вызове, даже если нет ни одного. Один дает греп, второй сам скрипт, третий фиг знает кто.

    Вариант 2.

    Тут уже захотелось сделать более гибкую штуку. А именно — взять например 1к урлов и раздать их поровну N потокам.

    > /dev/null 2>&1"); } if($i == $threads-1) { passthru("(php -f thread.php "".$perThread * $i."|".count($base)."" &) >> /dev/null 2>&1"); } if(($i !== 0)&&($i !== $threads-1)) { $start = $perThread * $i + 1; $end = $start -1 + $perThread; passthru("(php -f thread.php "".$start."|".$end."" &) >> /dev/null 2>&1"); } } ?>

    Данный пример запускает поток с параметрами, который тот потом достает из массива $argv и использует. Такое вот разделение труда.

    Взаимодействие между потоком и «мамой».
    Тут у меня только теория. Думаю реализовать это через файлы. Поток создает файл с именем своей сессии (номера, пида), мама читает оттуда. Чтобы вывести инфу о потоках красиво и наглядно, думаю заюзать ncurses . Это сишная либа, на ней сделаны midnight commander, top. Доступна не только для пхп.

    Изменения состояний потоков.
    Так и не придумал. Можно попробовать через теже файлы, но это надо после каждой строчки кода потока вставлять проверку файла на наличии там указаний. Скорость само собой упадет. Да и при таких реализациях мама убивается только вручную.

    Саня, он же похапэ дуд , сразу обрубил — Я бы такие задачи не решал на php. Я тоже посматриваю в сторону других языков, думаю выучить что-то типа си, но пока я выучу, мне наверно уже ничего не надо будет. Есть вариант на питоне. И как раз в ридере попалась статья одного камрада, который начал изучать его и

    Эта статья также доступна на следующих языках: Тайский

    • Next

      Огромное Вам СПАСИБО за очень полезную информацию в статье. Очень понятно все изложено. Чувствуется, что проделана большая работа по анализу работы магазина eBay

      • Спасибо вам и другим постоянным читателям моего блога. Без вас у меня не было бы достаточной мотивации, чтобы посвящать много времени ведению этого сайта. У меня мозги так устроены: люблю копнуть вглубь, систематизировать разрозненные данные, пробовать то, что раньше до меня никто не делал, либо не смотрел под таким углом зрения. Жаль, что только нашим соотечественникам из-за кризиса в России отнюдь не до шоппинга на eBay. Покупают на Алиэкспрессе из Китая, так как там в разы дешевле товары (часто в ущерб качеству). Но онлайн-аукционы eBay, Amazon, ETSY легко дадут китайцам фору по ассортименту брендовых вещей, винтажных вещей, ручной работы и разных этнических товаров.

        • Next

          В ваших статьях ценно именно ваше личное отношение и анализ темы. Вы этот блог не бросайте, я сюда часто заглядываю. Нас таких много должно быть. Мне на эл. почту пришло недавно предложение о том, что научат торговать на Амазоне и eBay. И я вспомнила про ваши подробные статьи об этих торг. площ. Перечитала все заново и сделала вывод, что курсы- это лохотрон. Сама на eBay еще ничего не покупала. Я не из России , а из Казахстана (г. Алматы). Но нам тоже лишних трат пока не надо. Желаю вам удачи и берегите себя в азиатских краях.

    • Еще приятно, что попытки eBay по руссификации интерфейса для пользователей из России и стран СНГ, начали приносить плоды. Ведь подавляющая часть граждан стран бывшего СССР не сильна познаниями иностранных языков. Английский язык знают не более 5% населения. Среди молодежи — побольше. Поэтому хотя бы интерфейс на русском языке — это большая помощь для онлайн-шоппинга на этой торговой площадке. Ебей не пошел по пути китайского собрата Алиэкспресс, где совершается машинный (очень корявый и непонятный, местами вызывающий смех) перевод описания товаров. Надеюсь, что на более продвинутом этапе развития искусственного интеллекта станет реальностью качественный машинный перевод с любого языка на любой за считанные доли секунды. Пока имеем вот что (профиль одного из продавцов на ебей с русским интерфейсом, но англоязычным описанием):
      https://uploads.disquscdn.com/images/7a52c9a89108b922159a4fad35de0ab0bee0c8804b9731f56d8a1dc659655d60.png