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

325

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

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

Показать ещё 2 комментария
Теги:
multithreading

18 ответов

357

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

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

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

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

Внимание: Расширение pthreads нельзя использовать в среде веб-сервера. Поэтому Threading в 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);
}
  • 3
    @Baba, я не могу настроить и установить pthreads на сервере Xampp. Вы можете помочь мне с этим?
  • 4
    Загрузите бинарный файл windows здесь windows.php.net/downloads/pecl/releases/pthreads/0.0.45
Показать ещё 12 комментариев
26

почему вы не используете 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]);
    }
}
  • 3
    Я использую решение выше, и работает отлично, я думаю, что это был самый простой способ сделать параллельный процесс с использованием php.
  • 59
    Вилка не нить.
Показать ещё 5 комментариев
15

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

С установкой тайм-аута завивки, установленной в 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);
  • 4
    В прошлый раз, когда я проверял (несколько лет назад), php не разрешал доступ к файловому хранилищу сессии двумя процессами одновременно. Он блокирует файл, и второй процесс должен сидеть в ожидании остановки первого скрипта. Я говорю о среде веб-сервера, а не CLI.
  • 1
    Его вилка не нить.
Показать ещё 1 комментарий
10

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

8

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

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

8

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

5

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

4

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

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

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

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

  • 0
    Это было пару лет назад, когда я столкнулся с этой проблемой и в конечном итоге сделал приложение, используя c ++, так как я также программирую на этом языке. Рассматривая это как альтернативное решение, я собираюсь опробовать его и посмотреть, насколько хорошо оно справляется.
3

Я знаю, что это устарело, но вы можете посмотреть http://phpthreadlib.sourceforge.net/

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

3

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

2

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

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

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

  • 0
    Это должен быть один ответ на это .
2

Как насчет pcntl_fork?

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

2

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

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

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

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

uname -a

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

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

0

Для многопоточности HTTP с помощью GuzzleHttp (поскольку pthreads могут не работать в веб-приложениях), см. Ниже блог - https://blog.madewithlove.be/post/concurrent-http-requests/

-2

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню