как можно использовать многопоточность в приложениях php

<p>существует ли реалистичный способ реализации многопоточной модели в php, будь то по-настоящему или просто имитировать ее. некоторое время назад было предложено заставить операционную систему загружать другой экземпляр исполняемого файла php и обрабатывать другие одновременные процессы.</p><p>проблема заключается в том, что когда код php завершил выполнение экземпляра php, он остается в памяти, потому что нет способа уничтожить его изнутри php. поэтому, если вы моделируете несколько потоков, вы можете представить, что произойдет. поэтому я все еще ищу способ, которым многопоточность может быть эффективно реализована или смоделирована изнутри php. есть идеи?</p>

<h1>многопоточность возможна в php</h1><p>да, вы можете выполнять многопоточность в php с помощью <a href="https://github.com/krakjoe/pthreads">pthreads</a> </p><p>из <a href="http://www.php.net/manual/en/intro.pthreads.php">документация php</a> :</p><blockquote> <p>pthreads - это объектно-ориентированный api, который предоставляет все инструменты, необходимые для многопоточности в php. php приложения могут создавать, читать, писать, выполнять и синхронизировать с потоками, рабочими и потоковыми объектами.</p> <p><strong> предупреждение </strong>: расширение pthreads нельзя использовать в среде веб-сервера. поэтому многопоточность в php должна оставаться только для приложений на основе cli.</p> </blockquote><p><strong> простой тест </strong></p><pre><code>#!/usr/bin/php <?php class asyncoperation extends thread { public function \_\_construct($arg) { $this->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(); } ?> </code></pre><p>первый забег</p><pre><code>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 </code></pre><p>второй прогон</p><pre><code>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 </code></pre><p><strong> пример из реального мира </strong></p><pre><code>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 urln", $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); } 

почему вы не используете <a href="https://secure.php.net/manual/de/function.popen.php">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]); } } </code></pre>

<p>потоки не доступны в стандартном php, но параллельное программирование возможно при использовании http-запросов в качестве асинхронных вызовов.</p><p>если для параметра curl timeout установлено значение 1 и используется один и тот же session\_id для процессов, которые вы хотите связать друг с другом, вы можете связываться с переменными сеанса, как в моем примере ниже. с помощью этого метода вы можете даже закрыть свой браузер, и параллельный процесс все еще существует на сервере.</p><p>не забудьте проверить правильный идентификатор сессии, как это:</p><blockquote> <p> <a href="http://localhost/test/verifysession.php?sessionid=[the">http: //localhost/test/verifysession.php? sessionid = [the</a> правильный идентификатор]</p> </blockquote><h3>startprocess.php</h3><pre><code>$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"]; </code></pre><h3>process1.php</h3><pre><code>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.phpif ($\_request["sessionid"]) session\_id($_request["sessionid"]); session_start(); var_dump($\_session); </code></pre><h3>closeprocess.php</h3><pre><code>if ($_request["sessionid"]) session_id($\_request["sessionid"]); session\_start(); $_session["closesession"] = true; var_dump($\_session); </code></pre>

<p>хотя вы не можете использовать потоки, у вас есть некоторый уровень контроля над процессами в php. здесь полезны два набора функций:</p><p>функции управления процессом <a href="http://www.php.net/manual/en/ref.pcntl.php">http://www.php.net/manual/en/ref.pcntl.php</a> </p><p>функции posix <a href="http://www.php.net/manual/en/ref.posix.php">http://www.php.net/manual/en/ref.posix.php</a> </p><p>вы можете обработать ваш процесс с помощью pcntl\_fork - возвращая pid дочернего процесса. затем вы можете использовать posix\_kill для удаления этого pid.</p><p>тем не менее, если вы убьете родительский процесс, то дочернему процессу должен быть послан сигнал о смерти. если сам php не распознает это, вы можете зарегистрировать функцию для управления им и выполнить чистый выход с помощью pcntl\_signal.</p>

<p>я знаю, что это старый вопрос, но для людей, ищущих, есть расширение pecl, написанное на c, которое теперь дает возможность многопоточности php, оно находится здесь <a href="https://github.com/krakjoe/pthreads">https://github.com/ krakjoe/pthreads</a> </p>

<p>использование потоков стало возможным благодаря расширению pthreads pecl</p><p> <a href="http://www.php.net/manual/en/book.pthreads.php">http://www.php.net/manual/en/book.pthreads.php</a> </p>

<p>вы могли бы симулировать потоки. php может запускать фоновые процессы через popen (или proc\_open). эти процессы могут быть переданы через stdin и stdout. конечно, эти процессы сами могут быть php-программами. это, вероятно, так близко, как вы получите.</p>

<p>вы можете использовать exec () для запуска сценария командной строки (например, php командной строки), и если вы передадите вывод в файл, тогда ваш сценарий не будет ждать завершения команды.</p><p>я не совсем помню синтаксис php cli, но вы бы хотели что-то вроде:</p><pre><code>exec("/path/to/php -f '/path/to/file.php' | '/path/to/output.txt'"); </code></pre><p>я думаю, что для многих серверов общего хостинга exec () отключен по умолчанию из соображений безопасности, но, возможно, стоит попробовать.</p>

<p>как насчет pcntl\_fork?</p><p>проверьте нашу страницу руководства для примеров: <a href="http://php.net/manual/en/function.pcntl-fork.php">php pcntl\_fork</a> </p>

<p>в зависимости от того, что вы пытаетесь сделать, вы также можете использовать curl\_multi для этого.</p>

<p>я знаю, что это старый, но вы можете посмотреть на <a href="http://phpthreadlib.sourceforge.net/">http://phpthreadlib.sourceforge.net/</a> </p><p>он поддерживает двунаправленную связь между потоками, а также имеет встроенную защиту для уничтожения дочерних потоков (предотвращения сирот).</p>

<p><code>pcntl\_fork</code> не будет работать в среде веб-сервера, если он <em>безопасный режим</em> включен. в этом случае он будет работать только в cli-версии php.</p>

<p>вы можете иметь возможность:</p><ol> <li>multi\_curl</li> <li>можно использовать системную команду для того же</li> <li>идеальный сценарий - создать функцию потоков на языке си и скомпилировать/настроить на php. теперь эта функция будет функцией php.</li> </ol>

<p> <a href="http://php.net/manual/en/class.thread.php">класс потока</a> доступен, так как pecl pthreads ≥ 2.0.0.</p>

<p>на момент написания моего текущего комментария я не знаю о потоках php. я сам пришел к поиску ответа, но один из обходных путей заключается в том, что программа php, которая получает запрос от веб-сервера, делегирует всю формулировку ответа консольному приложению, в котором хранится его вывод, ответ на запрос. запрос к двоичному файлу, и программа php, которая запустила консольное приложение, возвращает этот двоичный файл побайтно в качестве ответа на полученный запрос. консольное приложение может быть написано на любом языке программирования, работающем на сервере, включая те, которые имеют надлежащую поддержку потоков, включая программы на c++, использующие openmp.</p><p>один ненадежный, грязный трюк заключается в использовании php для выполнения консольного приложения, "uname",</p><pre><code>uname -a </code></pre><p>и распечатайте вывод этой консольной команды в вывод html, чтобы узнать точную версию серверного программного обеспечения. затем установите точно такую же версию программного обеспечения на экземпляр virtualbox, скомпилируйте/соберите все необходимые автономные, предпочтительно статические, двоичные файлы, а затем загрузите их на сервер. с этого момента приложение php может использовать эти двоичные файлы в роли консольного приложения, которое имеет правильную многопоточность. это грязный, ненадежный обходной путь в ситуации, когда администратор сервера не установил все необходимые реализации языка программирования на сервер. следует обратить внимание на то, что при каждом запросе приложение php получает консольное приложение (-я) и завершает работу/exit/get\_killed.</p><p>что касается того, что администраторы хостинг-службы думают о таких шаблонах использования сервера, я думаю, это сводится к культуре. в северной европе поставщику услуг необходимо предоставить то, что было объявлено, и если было разрешено выполнение консольных команд и разрешена загрузка файлов, не относящихся к вредоносным программам, и поставщик услуг имеет право уничтожить любой процесс сервера через несколько минут или даже через 30 секунд. то у администраторов хостинг-сервиса нет никаких аргументов для формирования правильной жалобы. в соединенных штатах и западной европе ситуация/культура очень разные, и я считаю, что есть большой шанс, что в сша и/или западной европе поставщик услуг хостинга откажется обслуживать клиентов услуг хостинга, которые используют описанный выше прием. это только мое предположение, учитывая мой личный опыт работы с хостинг-службами в сша и то, что я слышал от других о хостинг-сервисах в западной европе. на момент написания моего текущего комментария (2018\_09\_01) я ничего не знаю о культурных нормах южно-европейских провайдеров услуг хостинга, администраторов южно-европейских сетей.</p>