Як можна використовувати багатопотокове в PHP-додатках


414

Чи є реалістичний спосіб реалізації багатопотокової моделі в PHP, чи справді, чи просто її моделювання. Деякий час тому було запропоновано, що ви можете змусити операційну систему завантажити інший екземпляр виконуваного PHP та обробляти інші одночасні процеси.

Проблема в цьому полягає в тому, що після завершення коду PHP виконання екземпляра PHP залишається в пам'яті, оскільки немає можливості вбити його зсередини PHP. Отже, якщо ви імітуєте кілька потоків, ви можете уявити, що буде. Тому я все ще шукаю спосіб, як можна зробити багатоструйну чи ефективну імітацію в межах PHP. Будь-які ідеї?


1
Дивіться моє запитання тут: stackoverflow.com/questions/2101640 / ...
powtac


як використовувати розширення pthreads: phplobby.com/php-multi-thread-on-windows-pthreads-configuration
Емрах Мехмедов

Можливо, цікавить: pthreads.org
GibboK

Зараз у 2020 році здається, що "паралельно" php.net/manual/en/intro.parallel.php - те, що ми хочемо замість "pthreads": stackoverflow.com/a/56451969/470749
Ryan

Відповіді:


431

Можлива багатонарізка в php

Так, ви можете робити багатопотокові в PHP за допомогою pthreads

З документації PHP :

pthreads - це об'єктно-орієнтований API, який надає всі інструменти, необхідні для багатопотокової передачі в PHP. PHP-програми можуть створювати, читати, писати, виконувати та синхронізувати з об’єктами "Нитки", "Робітники" та "Нитка".

Попередження : Розширення 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);
}

3
@Baba, я не в змозі налаштувати та встановити pthreads на сервері Xampp. Чи можете ви мені в цьому допомогти?
Ірфан

4
Завантажте тут Windows binary тут windows.php.net/downloads/pecl/releases/pthreads/0.0.45
Баба

17
Це добре, я не торкався PHP роками, і тепер він має багатопотокові можливості!
крейсер

1
Приємно і просто! Просто FYI, я розгортаю додаток на сервері Azure Cloud Win, і якщо буде обрана лише основна конфігурація 1 ядра, багатопотокова передача буде недоступною, якщо не буде додано більше ядер.
Мілан

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

42

чому ти не використовуєш 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]);
    }
}

4
Я використовую рішення вище, і працює добре, я думаю, що це був найпростіший спосіб зробити паралельний процес за допомогою php.
GodFather

7
як, наприклад, @ e-info128, ця реалізація розщеплює процес, а це означає, що він працює в іншому процесі і не ділиться ресурсами процесу. Якщо говорити, якщо робота не потребує спільного використання ресурсів, це все одно буде працювати, і воно буде працювати паралельно.
Раффі

1
Як би ви передали змінні Popen без використання змінних сеансу?
atwellpub

@atwellpub Ні в якому разі, це окремі процеси, де немає ресурсів. Навіть сеанси будуть незручним механізмом IPC
Cholthi Paul Ttiopic

1
Для передачі даних їм можна також використовувати аргументи та сервер Redis.
Амір Фо

21

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

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.
Олексій Теніцький

5
set_time_limit(0);поступається! Ніколи, ніколи цього не роби.
Кафосо

@Kafoso Kafoso чому б і ні? Ну я згоден на PHP як процесор веб-скриптів, але чому б не в CLI? Якщо щось піде не так, CLI можна вбити за допомогою Ctrl + C ...
sijanec

14

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


1
Ця відповідь зараз дуже застаріла (що дуже справедливо, знаючи, що їй 11 років). Подивіться на pthreads нижче.
Мацей Папроцький

@MaciejPaprocki pThread тепер припинено зphp 7.4, замість цього використовуйте паралельно
Airy


10

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


pThread тепер відміняється від php 7.4, замість цього використовуйте паралельно
Airy

5

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

Я не можу повністю пригадати синтаксис PHP CLI, але ви хочете щось подібне:

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

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


4

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


3

Залежно від того, що ви намагаєтесь зробити, ви також можете використовувати curl_multi, щоб досягти цього.


3

Я знаю, що це вже давно, але ви можете подивитися http://phpthreadlib.sourceforge.net/

Він підтримує двонаправлену взаємодію між нитками, а також має вбудований захист для вбивання дочірніх ниток (запобігання сиротам).


3

Ви можете мати варіант:

  1. multi_curl
  2. Можна використовувати системну команду для того ж
  3. Ідеальний сценарій полягає в тому, щоб створити функцію нарізки мовою C та компілювати / конфігурувати в PHP. Тепер ця функція буде функцією PHP.

3

Клас Thread доступний з PECL pthreads ≥ 2.0.0.


1
Чи можу я запустити потік HTTP? просто: yourname.com/thread.php?
Моє ім’я

pThread тепер відміняється від php 7.4, замість цього використовуйте паралельно
Airy

3

Як щодо 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
    }

?>

2

pcntl_forkне працюватиме в середовищі веб-сервера, якщо ввімкнений безпечний режим . У цьому випадку вона працюватиме лише у CLI-версії PHP.


1

Якщо ви використовуєте сервер 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();

-1

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

Один ненадійний, брудний фокус - використовувати PHP для виконання консольного додатка, "уніме",

uname -a

і надрукувати вихід цієї команди консолі на вихід HTML, щоб дізнатися точну версію серверного програмного забезпечення. Потім встановіть таку саму версію програмного забезпечення в екземпляр VirtualBox, компілюйте / збирайте будь-які повністю автономні, бажано статичні, бінарні файли, які хочете, а потім завантажуйте їх на сервер. З цього моменту додаток PHP може використовувати ті двійкові файли в ролі консольного додатка, який має належну багатопотоковість. Це брудне, ненадійне вирішення ситуації, коли адміністратор сервера не встановив усі необхідні реалізації мови програмування на сервері. На що слід звернути увагу, це те, що на кожен запит, що програма PHP отримує консольну програму, припиняє / виходить / get_killed.

Щодо того, що адміністратори хостингових послуг думають про такі схеми використання сервера, я думаю, це зводиться до культури. У Північній Європі постачальник послуг ДОБАВЛЯЄТЬСЯ, ЩО РОЗМІСТУВАЛИ, і якщо виконання команд консолі було дозволено, а завантаження файлів, що не містять зловмисне програмне забезпечення, було дозволено, і постачальник послуг має право знищити будь-який серверний процес через кілька хвилин або навіть через 30 секунд. , то адміністраторам служб хостингу не вистачає аргументів для формування належної скарги. У Сполучених Штатах та Західній Європі ситуація / культура є дуже різною, і я вважаю, що існує велика ймовірність того, що в США та / або Західній Європі постачальник послуг хостингу відмовиться обслуговувати клієнтів хостингу, які використовують вищеописаний трюк. Це лише моя здогадка, враховуючи мій особистий досвід із США хостингові послуги та дано те, що я чув від інших про західноєвропейські хостингові послуги. Щодо написання мого нинішнього коментаря (2018_09_01) я нічого не знаю про культурні норми південно-європейських постачальників послуг хостингу, адміністраторів південно-європейських мереж.


-3

Можливо, я щось пропустив, але exec не працював як асинхронний для мене в середовищі Windows, я використовував наступні в 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 () завершиться). У своєму коді ви закриваєте кожен потік відразу після відкриття, щоб ваш код не поводився як багатопотоковий.
Кмейкснер

-5

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

Цей хлопець зробив дуже гарну роботу Multithreading у php


Крім того, це не має нічого спільного з MultiThreading. Це паралельна обробка. Зовсім різні речі.
Digital Human

На мою думку, як спосіб вирішення, надзвичайний злом, ідея запропонованого рішення є дуже доречною, але, мабуть, у різних людей можуть бути вогняні війни з приводу того, що являє собою "справжню багатопотоковість", тому що існує відмінність між паралельністю та технікою на основі обладнання паралельна обробка, як описано на сайті: youtube.com/watch?v=cN_DpYBzKso
Мартін Вагі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.