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

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

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

Смотрите мой вопрос и ответы здесь: stackoverflow.com/questions/2101640/…

powtac 05.05.2012 02:11

... и мой здесь: stackoverflow.com/questions/209774/does-php-have-threading/…

Francois Bourgeois 08.01.2013 16:01

как использовать расширение pthreads: phplobby.com/php-multi-thread-on-windows-pthreads-configurat‌ ion

Emrah Mehmedov 20.03.2014 15:25

Может интересно: pthreads.org

GibboK 06.01.2016 14:09

Теперь, в 2020 году, кажется, что «параллельный» php.net/manual/en/intro.parallel.php - это то, что нам нужно вместо «pthreads»: stackoverflow.com/a/56451969/470749

Ryan 09.03.2020 22:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
442
5
505 911
17

Ответы 17

Хотя вы не можете использовать потоки, у вас есть некоторая степень контроля над процессами в 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.

Этот ответ сейчас очень устарел (что вполне справедливо, учитывая, что ему 11 лет). Посмотрите на pthreads ниже.

Maciej Paprocki 10.07.2019 17:49

@MaciejPaprocki pThread больше не поддерживается с PHP 7.4, вместо этого используйте параллельный

Airy 31.03.2020 15:57

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

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

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

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

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

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

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

http://localhost/test/verifysession.php?sessionid=[the correct id]

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

Последний раз я проверял (несколько лет назад), что php не разрешал доступ к файловому хранилищу сеансов одновременно двум процессам. Он блокирует файл, и второй процесс должен ждать остановки первого скрипта. Я говорю о среде веб-сервера, а не о CLI.

Alexei Tenitski 06.05.2011 01:15

set_time_limit(0); yikes! Никогда, никогда не делай этого.

Kafoso 27.05.2016 14:57

@Kafoso Kafoso, почему бы и нет? Хорошо, я согласен с PHP как обработчиком веб-скриптов, но почему не в CLI? Если что-то пойдет не так, CLI можно убить с помощью Ctrl + C ...

sijanec 01.09.2019 02:57

Я бы также использовал set_time_limit(0); для бесконечно запущенных слушателей сокетов.

Jay Dadhania 18.09.2020 07:20

set_time_limitобновляет тайм-аут, поэтому было бы разумно поместить его в цикл while с некоторым адекватным таймаутом, скажем здесь 30 секунд.

TechNyquist 30.12.2020 15:25

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

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

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

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

GodFather 07.03.2012 18:31

как сказал @ e-info128, эта реализация разветвляет процесс, что означает, что он работает в другом процессе и не разделяет ресурсы процесса. При этом, если выполняемое задание не требует совместного использования ресурсов, это все равно будет работать, и оно будет выполняться параллельно.

Raffi 10.01.2017 13:23

Как бы вы передавали переменные в popen без использования переменных сеанса?

atwellpub 13.03.2017 03:39

@atwellpub Ни в коем случае, это отдельные процессы, не разделяющие ресурсов. Даже сеансы будут неудобным механизмом IPC

Cholthi Paul Ttiopic 23.10.2017 09:10

@ e-info128 что за вилка?

MAZux 14.10.2018 16:52

Для передачи им данных вы также можете использовать аргументы и сервер Redis.

Amir Fo 06.03.2019 09:37

@MAZux Форк - это обычно дочерний процесс, порожденный родителем. Итак, здесь предлагается, чтобы однопоточное приложение PHP могло создать другой процесс PHP (вилку) и использовать его как подход поддельной потоковой передачи.

Greg Hilston 03.05.2019 23:25

У меня есть скрипт cron, который запускается каждую минуту. Список задач стал достаточно большим, поэтому на его выполнение требуется более минуты. Это было бы идеально для разделения задач без ручного добавления скриптов cron. Спасибо.

user2924019 01.09.2020 11:28

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

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

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

Как насчет 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
    }

?>

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

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

pThread больше не поддерживается с php 7.4, вместо этого используйте параллельный

Airy 31.03.2020 15:58

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

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

От документация PHP:

pthreads is an object-orientated API that provides all of the tools needed for multi-threading in PHP. PHP applications can create, read, write, execute and synchronize with Threads, Workers and Threaded objects.

Warning: The pthreads extension cannot be used in a web server environment. Threading in PHP should therefore remain to CLI-based applications only.

Простой тест

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

@Baba, я не могу настроить и установить pthreads на сервере Xampp. Вы можете мне с этим помочь?

Irfan 14.09.2013 15:03

Загрузите двоичный файл Windows здесь windows.php.net/downloads/pecl/releases/pthreads/0.0.45

Baba 04.10.2013 03:31

@Baba можно ли получить доступ к общим переменным SESSION с помощью многопоточности?

Mehdi Karamosly 01.11.2013 21:22

@danip - рабочий MPM для Apache не имеет ничего общего с потоками пользовательской среды PHP, о чем говорит Баба. Это две совершенно разные вещи. Рабочий MPM для Apache не предоставляет никаких функциональных возможностей многопоточности для PHP-программистов с точки зрения разделения единиц обработки по потокам, а затем join () - объединения их с основным контекстом, когда они будут выполнены. MPM просто более эффективно распределяет запросы между процессами / потоками, на которых выполняется процесс PHP.

N.B. 17.12.2013 16:47

Это хорошо, я годами не касался PHP, и теперь у него есть возможности многопоточности!

cruizer 27.03.2014 12:33

Красиво и просто! К вашему сведению, я развертываю приложение на сервере Azure Cloud Win, и если выбрана только базовая конфигурация с одним ядром, многопоточность будет недоступна, если не будет добавлено больше ядер.

Milan 27.05.2014 21:57

У меня возникли проблемы с поиском ответа на свой вопрос, поэтому я опубликую здесь ... (PHP кажется очень эмоционально заряженным языком, и уровень субъективной риторики по этому поводу ... ну ... расстраивает, я отвлекся. ..) В документации упоминается, что он позволяет создавать потоки на уровне пользователя. У меня вопрос, а как насчет ядра? Является ли PHP действительно многопоточным или он предназначен исключительно для пользователей? Мы также будем очень благодарны за документацию по этому вопросу.

taylorcressy 24.09.2014 00:34

Пожалуйста, посмотрите здесь stackoverflow.com/questions/28493421/… Я пытаюсь работать с многопоточностью, но не уверен, что это работает и занимает больше времени, чем ожидалось.

Raheel 13.02.2015 10:02

@Baba iam с использованием системы ubuntu 14.04. как настроить в нем pthread.

Abhijit Jagtap 15.07.2016 20:57

Допустим, у вас есть процесс php, который порождает несколько потоков для выполнения некоторых операций, которые также могут включать вызовы БД. Теперь, если есть исключение SQL в 1 потоке, вызывает ли сбой всю систему?

discoverAnkit 20.10.2016 18:18

Почему существует отказ от использования pthreads для Интернета и использования только для CLI secure.php.net/manual/en/intro.pthreads.php

Amr ElAdawy 16.02.2017 09:31

@Baba могу ли я запустить ветку по HTTP? просто: yourname.com/thread.php?

My Name 19.08.2017 10:58

В нем говорится, что требуется версия 7.2+, но загрузка возможна только для версии 7.1.9. Я использую win32 cli php на 7.1.9, как я могу это использовать?

Leandro 08.09.2017 02:43

В PHP 7.2.4 (cli) ( ZTS MSVC15 (Visual C++ 2017) x64 ) я просто вижу start и не повторяю finish, почему ?!

Nabi K.A.Z. 02.05.2019 03:11

Будьте осторожны: Джо Уоткинс, автор расширения pthreads, прекратил разработку в пользу нового параллельного расширения: github.com/krakjoe/pthreads/issues/929

Anton Belonovich 01.12.2019 19:53

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

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

pclose(popen("start /B ". $script_exec, "r"));

Чтобы взломать многопоточность в PHP, вы должны открыть несколько операторов popen () (PHP не будет ждать его завершения). Затем после того, как полдюжины или около того открыты, вы вызываете для них pclose () (PHP будет ждать завершения pclose ()). В вашем коде вы закрываете каждый поток сразу после его открытия, поэтому ваш код не будет вести себя как многопоточный.

Kmeixner 19.06.2014 21:49

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

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

Многопоточность означает одновременное выполнение нескольких задач или процессов, мы можем добиться этого на 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

Кроме того, это не имеет ничего общего с MultiThreading. Это параллельная обработка. Совершенно разные вещи.

Digital Human 11.12.2017 17:28

На мой взгляд, в качестве обходного пути, экстренного взлома идея предлагаемого решения очень уместна, но я предполагаю, что у разных людей могут быть ожесточенные войны по поводу того, что представляет собой «настоящая многопоточность», потому что существует различие между параллелизмом и аппаратным обеспечением. параллельная обработка, как описано в: youtube.com/watch?v=cN_DpYBzKso

Martin Vahi 01.09.2018 08:33

Класс потока доступен, так как pthreads PECL ≥ 2.0.0.

могу ли я запустить поток по HTTP? просто: yourname.com/thread.php?

My Name 19.08.2017 15:28

pThread больше не поддерживается с php 7.4, вместо этого используйте параллельный

Airy 31.03.2020 15:59

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

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

uname -a

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

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

Если вы используете сервер 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();

Другие вопросы по теме