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

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

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


person Steve Obbayi    schedule 16.09.2008    source источник
comment
См. Мой вопрос и ответы здесь: stackoverflow.com/questions/ 2101640 /   -  person powtac    schedule 05.05.2012
comment
... и мой здесь: stackoverflow.com/questions / 209774 / does-php-have-threading /   -  person Francois Bourgeois    schedule 08.01.2013
comment
как использовать расширение pthreads: phplobby.com/php-multi-thread -on-windows-pthreads-configuration   -  person Emrah Mehmedov    schedule 20.03.2014
comment
Может быть интересно: pthreads.org   -  person GibboK    schedule 06.01.2016
comment
Теперь, в 2020 году, кажется, что параллельный php.net/manual/en/intro.parallel .php - это то, что нам нужно вместо pthreads: stackoverflow.com/a/56451969/470749   -  person Ryan    schedule 09.03.2020


Ответы (16)


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

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

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

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

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

Простой тест

#!/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();
}

?>

Первый забег

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);
}
person Baba    schedule 19.03.2013
comment
@Baba, я не могу настроить и установить pthreads на сервере Xampp. Вы можете мне с этим помочь? - person Irfan; 14.09.2013
comment
Загрузите двоичный файл Windows здесь windows.php.net/downloads/pecl/releases /pthreads/0.0.45 - person Baba; 04.10.2013
comment
@Baba можно ли получить доступ к общим переменным SESSION с помощью многопоточности? - person Mehdi Karamosly; 01.11.2013
comment
@danip - рабочий MPM для Apache не имеет ничего общего с потоками пользовательской среды PHP, о чем говорит Баба. Это две совершенно разные вещи. MPM worker для Apache не предоставляет программистам PHP каких-либо потоковых функций в плане разделения единиц обработки по потокам, а затем join () - объединения их с основным контекстом, когда они будут выполнены. MPM просто более эффективно распределяет запросы между процессами / потоками, на которых выполняется процесс PHP. - person N.B.; 17.12.2013
comment
Это хорошо, я годами не касался PHP, и теперь у него есть возможности многопоточности! - person cruizer; 27.03.2014
comment
Красиво и просто! К вашему сведению, я развертываю приложение на сервере Azure Cloud Win, и если выбрана только базовая конфигурация с одним ядром, многопоточность будет недоступна, если не будет добавлено больше ядер. - person Milan; 27.05.2014
comment
У меня возникли проблемы с поиском ответа на свой вопрос, поэтому я опубликую здесь ... (PHP кажется очень эмоционально заряженным языком, и уровень субъективной риторики по этому поводу ... ну ... расстраивает, я отвлекся. ..) В документации упоминается, что он позволяет создавать потоки на уровне пользователя. У меня вопрос, а как насчет ядра? Является ли PHP действительно многопоточным или он предназначен исключительно для пользователей? Мы также будем очень благодарны за документацию по этому вопросу. - person taylorcressy; 24.09.2014
comment
Пожалуйста, посмотрите здесь stackoverflow.com/questions/28493421/ Я пытаюсь работать с многопоточностью, но не уверен, что это работает и занимает больше времени, чем ожидалось. - person Raheel; 13.02.2015
comment
@Baba iam с использованием системы ubuntu 14.04. как настроить в нем pthread. - person Abhijit Jagtap; 15.07.2016
comment
Допустим, у вас есть процесс php, который порождает несколько потоков для выполнения некоторых операций, которые также могут включать вызовы БД. Теперь, если есть исключение SQL в 1 потоке, вызывает ли сбой всю систему? - person discoverAnkit; 20.10.2016
comment
Почему существует отказ от использования pthreads для Интернета и использования только для CLI secure.php.net/manual/en/intro.pthreads.php - person Amr ElAdawy; 16.02.2017
comment
@Baba могу ли я запустить ветку по HTTP? просто: yourname.com/thread.php? - person My Name; 19.08.2017
comment
В нем говорится, что требуется версия 7.2+, но загрузка возможна только для версии 7.1.9. Я использую win32 cli php на 7.1.9, как я могу это использовать? - person Leandro Bardelli; 08.09.2017
comment
В PHP 7.2.4 (cli) ( ZTS MSVC15 (Visual C++ 2017) x64 ) я просто вижу start и не повторяю finish почему ?! - person Nabi K.A.Z.; 02.05.2019
comment
Будьте осторожны: Джо Уоткинс, автор расширения pthreads, прекратил разработку в пользу нового параллельного расширения: github.com/krakjoe/pthreads/issues/929 - person Anton Belonovich; 01.12.2019
comment
pThreads больше не выпускается. Используйте parallel из PHP 7.4. - person T.Todua; 30.05.2021
comment
OTOH, для параллельной работы требуется перекомпилированная версия PHP, насколько я понимаю? огромная разрушительная задача, если у вас уже есть рабочий сервер ... есть ли другие возможные альтернативы? - person Jose Cifuentes; 04.06.2021

почему бы вам не использовать 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]);
    }
}
person masterb    schedule 03.12.2010
comment
Я использую вышеприведенное решение и отлично работает, я думаю, что это был самый простой способ выполнить параллельный процесс с помощью php. - person GodFather; 07.03.2012
comment
как сказал @ e-info128, эта реализация разветвляет процесс, что означает, что он работает в другом процессе и не разделяет ресурсы процесса. При этом, если текущее задание не требует совместного использования ресурсов, то это все равно будет работать, и оно будет выполняться параллельно. - person Raffi; 10.01.2017
comment
Как бы вы передавали переменные в popen без использования переменных сеанса? - person atwellpub; 13.03.2017
comment
@atwellpub Ни в коем случае, это отдельные процессы, не разделяющие ресурсов. Даже сеансы будут неудобным механизмом IPC - person Cholthi Paul Ttiopic; 23.10.2017
comment
@ e-info128 что за вилка? - person MAZux; 14.10.2018
comment
Для передачи им данных вы также можете использовать аргументы и сервер Redis. - person Amir Fo; 06.03.2019
comment
@MAZux Форк - это обычно дочерний процесс, порожденный родителем. Итак, здесь предлагается, чтобы однопоточное приложение PHP могло создать другой процесс PHP (вилку) и использовать его как подход поддельной потоковой передачи. - person Greg Hilston; 03.05.2019
comment
У меня есть скрипт cron, который запускается каждую минуту. Список задач стал достаточно большим, поэтому на его выполнение требуется более минуты. Это было бы идеально для разделения задач без ручного добавления скриптов cron. Спасибо. - person user2924019; 01.09.2020
comment
Я пробовал этот код, но получаю сообщение об ошибке sh: 1: script2.php: not found. Файл действительно существует. Я также пробовал полный путь к файлу, та же ошибка. Есть идеи, что может быть не так? - person Wyatt Jackson; 22.06.2021

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

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

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

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

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);
}

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);
person Community    schedule 03.07.2009
comment
В последний раз я проверял (несколько лет назад), что php не разрешал доступ к файловому хранилищу сеансов одновременно двум процессам. Он блокирует файл, и второй процесс должен ждать остановки первого скрипта. Я говорю о среде веб-сервера, а не о CLI. - person Alexei Tenitski; 06.05.2011
comment
set_time_limit(0); ура! Никогда, никогда не делай этого. - person Kafoso; 27.05.2016
comment
@Kafoso Kafoso, почему бы и нет? Хорошо, я согласен с PHP как обработчиком веб-скриптов, но почему не в CLI? Если что-то пойдет не так, CLI можно убить с помощью Ctrl + C ... - person sijanec; 01.09.2019
comment
Я бы также использовал set_time_limit(0); для бесконечно запущенных слушателей сокетов. - person Jay Dadhania; 18.09.2020
comment
set_time_limit обновляет timeout, поэтому было бы разумно поместить его в цикл while с некоторым адекватным таймаутом, скажем здесь 30 секунд. - person TechNyquist; 30.12.2020

Хотя вы не можете использовать потоки, у вас есть некоторая степень контроля над процессом в php. Здесь могут быть полезны два набора функций:

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

Функции POSIX http://www.php.net/manual/en/ref.posix.php

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

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

person J.D. Fitz.Gerald    schedule 16.09.2008
comment
Этот ответ сейчас очень устарел (что вполне справедливо, учитывая, что ему 11 лет). Посмотрите на pthreads ниже. - person Maciej Paprocki; 10.07.2019
comment
@MaciejPaprocki pThread больше не поддерживается с PHP 7.4, вместо этого используйте параллельный - person Airy; 31.03.2020

использование потоков стало возможным благодаря расширению pthreads PECL

http://www.php.net/manual/en/book.pthreads.php

person pinkal vansia    schedule 31.10.2013
comment
pThread больше не поддерживается с php 7.4, вместо этого используйте parallel. - person T.Todua; 30.05.2021

Я знаю, что это старый вопрос, но для людей, ищущих, есть расширение PECL, написанное на C, которое теперь дает возможность многопоточности PHP, оно находится здесь https://github.com/krakjoe/pthreads

person JasonDavis    schedule 19.09.2012
comment
pThread больше не поддерживается с php 7.4, вместо этого используйте параллельный - person Airy; 31.03.2020

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

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

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

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

person Adam Hopkinson    schedule 16.09.2008

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

person Pete    schedule 26.08.2010

Вы можете выбрать:

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

Как насчет pcntl_fork?

примеры можно найти на нашей странице руководства: PHP pcntl_fork

<?php

    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        pcntl_wait($status); //Protect against Zombie children
    } else {
        // we are the child
    }

?>
person Jarrod    schedule 13.01.2012

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

person Sheldmandu    schedule 25.01.2011

Если вы используете сервер Linux, вы можете использовать

exec("nohup $php_path path/script.php > /dev/null 2>/dev/null &")

Если вам нужно передать некоторые аргументы

exec("nohup $php_path path/script.php $args > /dev/null 2>/dev/null &")

В script.php

$args = $argv[1];

Или используйте Symfony https://symfony.com/doc/current/components/process.html < / а>

$process = Process::fromShellCommandline("php ".base_path('script.php'));
$process->setTimeout(0);     
$process->disableOutput();     
$process->start();
person Юрий Ярвинен    schedule 29.04.2020

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

person Stilero    schedule 02.02.2012

Может быть, я что-то пропустил, но exec не работал для меня асинхронно в среде Windows, которую я использовал в Windows, и это работало как шарм;)

$script_exec = "c:/php/php.exe c:/path/my_ascyn_script.php";

pclose(popen("start /B ". $script_exec, "r"));
person Mubashar    schedule 03.10.2013
comment
Чтобы взломать многопоточность в PHP, вы должны открыть несколько операторов popen () (PHP не будет ждать его завершения). Затем после того, как полдюжины или около того открыты, вы вызываете для них pclose () (PHP будет ждать завершения pclose ()). В вашем коде вы закрываете каждый поток сразу после его открытия, поэтому ваш код не будет вести себя как многопоточный. - person Kmeixner; 19.06.2014

На момент написания моего текущего комментария я ничего не знал о потоках PHP. Я сам пришел искать здесь ответ, но один обходной путь заключается в том, что программа PHP, которая получает запрос от веб-сервера, делегирует всю формулировку ответа консольному приложению, которое сохраняет свой вывод, ответ на запрос, в двоичный файл. а программа PHP, запустившая консольное приложение, побайтно возвращает этот двоичный файл в качестве ответа на полученный запрос. Консольное приложение может быть написано на любом языке программирования, который работает на сервере, включая те, которые имеют надлежащую поддержку потоковой передачи, включая программы C ++, использующие OpenMP.

Один ненадежный, грязный трюк - использовать PHP для выполнения консольного приложения uname,

uname -a

и распечатайте вывод этой консольной команды в вывод HTML, чтобы узнать точную версию серверного программного обеспечения. Затем установите ту же версию программного обеспечения в экземпляр VirtualBox, скомпилируйте / соберите любые полностью автономные, предпочтительно статические двоичные файлы, которые вам нужны, и затем загрузите их на сервер. С этого момента приложение PHP может использовать эти двоичные файлы в роли консольного приложения, которое имеет правильную многопоточность. Это грязный, ненадежный обходной путь в ситуации, когда администратор сервера не установил на сервере все необходимые реализации языка программирования. Следует обратить внимание на то, что при каждом запросе, который приложение PHP получает, консольное приложение (я) завершает работу / exit / get_killed.

Что касается того, что администраторы хостинга думают о таких моделях использования серверов, я полагаю, что это связано с культурой. В Северной Европе поставщик услуг ДОЛЖЕН ПРЕДОСТАВЛЯТЬ ТО, ЧТО БЫЛО ОБЪЯВЛЕНО, и если было разрешено выполнение консольных команд и разрешена загрузка файлов, не связанных с вредоносными программами, и поставщик услуг имеет право убить любой серверный процесс через несколько минут или даже через 30 секунд. , то у администраторов хостинга нет никаких аргументов для формирования надлежащей жалобы. В Соединенных Штатах и ​​Западной Европе ситуация / культура сильно различаются, и я считаю, что есть большая вероятность, что в США и / или Западной Европе поставщик услуг хостинга откажется обслуживать клиентов, использующих описанный выше трюк. Это только мое предположение, учитывая мой личный опыт работы с услугами хостинга в США и то, что я слышал от других о западноевропейских услугах хостинга. На момент написания моего текущего комментария (2018_09_01) я ничего не знал о культурных нормах южно-европейских хостинг-провайдеров и администраторов южно-европейских сетей.

person Martin Vahi    schedule 01.09.2018

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

chdir(dirname(__FILE__));  //if you want to run this file as cron job
 for ($i = 0; $i < 2; $i += 1){
 exec("php test_1.php $i > test.txt &");
 //this will execute test_1.php and will leave this process executing in the background and will go         

 //to next iteration of the loop immediately without waiting the completion of the script in the   

 //test_1.php , $i  is passed as argument .

}

Test_1.php

$conn=mysql_connect($host,$user,$pass);
$db=mysql_select_db($db);
$i = $argv[1];  //this is the argument passed from index.php file
for($j = 0;$j<5000; $j ++)
{
mysql_query("insert  into  test   set

                id='$i',

                comment='test',

                datetime=NOW() ");

}

Это выполнит test_1.php два раза одновременно, и оба процесса будут работать в фоновом режиме одновременно, так что таким образом вы можете добиться многопоточности в php.

Этот парень проделал действительно хорошую работу. Многопоточность в php

person Pir Abdul    schedule 05.11.2013
comment
Кроме того, это не имеет ничего общего с MultiThreading. Это параллельная обработка. Совершенно разные вещи. - person Digital Human; 11.12.2017
comment
На мой взгляд, в качестве обходного пути, экстренного взлома, идея предлагаемого решения очень уместна, но я предполагаю, что у разных людей могут быть пламенные войны о том, что представляет собой настоящая многопоточность, потому что существует различие между параллелизмом и параллельной обработкой на аппаратной основе. , как описано на странице: youtube.com/watch?v=cN_DpYBzKso - person Martin Vahi; 01.09.2018