Jak korzystać z wielowątkowości w aplikacjach PHP


414

Czy istnieje realistyczny sposób implementacji modelu wielowątkowego w PHP, czy to naprawdę, czy tylko symulując go. Jakiś czas temu zasugerowano, że można zmusić system operacyjny do załadowania innej instancji pliku wykonywalnego PHP i obsługiwać inne jednoczesne procesy.

Problem polega na tym, że po zakończeniu wykonywania kodu PHP instancja PHP pozostaje w pamięci, ponieważ nie ma możliwości zabicia go z poziomu PHP. Więc jeśli symulujesz kilka wątków, możesz sobie wyobrazić, co się stanie. Wciąż szukam sposobu, w jaki wielowątkowość może być efektywnie wykonana lub symulowana z poziomu PHP. Jakieś pomysły?


1
Zobacz moje pytanie i odpowiedzi tutaj: stackoverflow.com/questions/2101640/…
powtac



Może interesujące: pthreads.org
GibboK

Teraz w 2020 roku wydaje się, że „równoległy” php.net/manual/en/intro.parallel.php jest tym, czego chcemy zamiast „pthreads”: stackoverflow.com/a/56451969/470749
Ryan

Odpowiedzi:


431

Wielowątkowość jest możliwa w php

Tak, możesz robić wielowątkowość w PHP za pomocą pthreads

Z dokumentacji PHP :

pthreads to zorientowany obiektowo interfejs API, który zapewnia wszystkie narzędzia potrzebne do wielowątkowości w PHP. Aplikacje PHP mogą tworzyć, czytać, pisać, uruchamiać i synchronizować z wątkami, pracownikami i wątkami.

Ostrzeżenie : rozszerzenia pthreads nie można używać w środowisku serwera WWW. Wątki w PHP powinny zatem pozostać tylko w aplikacjach opartych na CLI.

Prosty test

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

?>

Pierwszy bieg

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

Drugi bieg

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

Przykład świata rzeczywistego

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, nie jestem w stanie skonfigurować i zainstalować pthreads na serwerze Xampp. Czy możesz mi z tym pomóc?
Irfan

4
Pobierz binarny system Windows tutaj windows.php.net/downloads/pecl/releases/pthreads/0.0.45
Baba

17
To miłe, nie dotykałem PHP od lat, a teraz ma możliwości wielowątkowości!
cruizer

1
Ładnie i prosto! Tylko do Twojej wiadomości, wdrażam aplikację na serwerze Azure Cloud Win i jeśli zostanie wybrana tylko podstawowa konfiguracja 1-rdzeniowa, wielowątkowość nie będzie dostępna, dopóki nie zostanie dodanych więcej rdzeni.
Mediolan

3
Uwaga: Joe Watkins, autor rozszerzenia pthreads, przerwał prace nad nowym równoległym rozszerzeniem: github.com/krakjoe/pthreads/issues/929
Anton Belonovich,

42

dlaczego nie używasz 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
Używam powyższego rozwiązania i działa dobrze, myślę, że był to najłatwiejszy sposób na wykonanie równoległego procesu przy użyciu php.
GodFather

7
jak powiedział @ e-info128, ta implementacja rozwidla proces, co oznacza, że ​​działa on na innym procesie i nie współużytkuje zasobów procesu. Biorąc to pod uwagę, jeśli dane zadanie nie musi współużytkować zasobów, to nadal będzie działać i będzie działać równolegle.
Raffi,

1
Jak przekazałeś zmienne do popen bez używania zmiennych sesji?
atwellpub

@atwellpub Nie ma mowy, są to osobne procesy, które nie współużytkują zasobów. Nawet sesje będą niezręcznym mechanizmem IPC
Cholthi Paul Ttiopic

1
Aby przekazać im dane, możesz użyć argumentów i serwera Redis.
Amir Fo

21

Wątek nie jest dostępny w magazynie PHP, ale możliwe jest równoczesne programowanie przy użyciu żądań HTTP jako wywołań asynchronicznych.

Po ustawieniu limitu czasu curl na 1 i użyciu tego samego session_id dla procesów, które chcesz ze sobą powiązać, możesz komunikować się ze zmiennymi sesji jak w moim przykładzie poniżej. Dzięki tej metodzie możesz nawet zamknąć przeglądarkę, a na serwerze nadal istnieje współbieżny proces.

Nie zapomnij zweryfikować poprawnego identyfikatora sesji w ten sposób:

http: //localhost/test/verifysession.php? sessionid = [ poprawny identyfikator]

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"];

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

Veritsession.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
Ostatnim razem, gdy sprawdziłem (kilka lat temu), PHP nie pozwoliło na dostęp do przechowywania sesji opartego na plikach przez dwa procesy jednocześnie. Blokuje plik i drugi proces musi tam siedzieć i czekać na zatrzymanie pierwszego skryptu. Mówię o środowisku serwera WWW, a nie CLI.
Aleksiej Tenitski

5
set_time_limit(0);yikes! Nigdy, nigdy tego nie rób.
Kafoso

@Kafoso Kafoso dlaczego nie? Zgadzam się na PHP jako procesor skryptów internetowych, ale dlaczego nie w CLI? Jeśli coś pójdzie nie tak, CLI można zabić za pomocą Ctrl + C ...
sijanec

14

Chociaż nie możesz wątkować, masz pewien stopień kontroli procesu w php. Dwa przydatne tutaj zestawy funkcji to:

Funkcje kontroli procesu http://www.php.net/manual/en/ref.pcntl.php

Funkcje POSIX http://www.php.net/manual/en/ref.posix.php

Możesz rozwidlić swój proces za pomocą pcntl_fork - zwracając PID dziecka. Następnie możesz użyć posix_kill, aby uzyskać ten PID.

To powiedziawszy, jeśli zabijesz proces nadrzędny, do procesu potomnego należy wysłać sygnał z informacją o śmierci. Jeśli sam php nie rozpoznaje tego, możesz zarejestrować funkcję do zarządzania nim i zrobić czyste wyjście używając pcntl_signal.


1
Ta odpowiedź jest teraz bardzo nieaktualna (co jest bardzo uczciwe, biorąc pod uwagę, że ma 11 lat). Spójrz na pthreads poniżej.
Maciej Paprocki,

@MaciejPaprocki pThread jest teraz wycofany z programu 7.4 zamiast używać równoległego
Airy


10

Wiem, że to stare pytanie, ale dla osób szukających istnieje rozszerzenie PECL napisane w C, które daje teraz możliwość wielowątkowości PHP, znajduje się tutaj https://github.com/krakjoe/pthreads


pThread jest teraz wycofany z php 7.4 zamiast tego używaj równolegle
Airy

5

Możesz użyć exec () do uruchomienia skryptu wiersza poleceń (takiego jak php wiersza poleceń), a jeśli potokujesz wyjście do pliku, twój skrypt nie będzie czekał na zakończenie polecenia.

Nie do końca pamiętam składnię CLI CLP, ale chciałbyś czegoś takiego:

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

Myślę, że sporo współużytkowanych serwerów hostingowych ma domyślnie wyłączone exec () ze względów bezpieczeństwa, ale warto spróbować.


4

Możesz symulować wątki. PHP może uruchamiać procesy w tle za pośrednictwem popen (lub proc_open). Procesy te można komunikować za pomocą stdin i stdout. Oczywiście procesy te same mogą być programami php. Prawdopodobnie jest tak blisko, jak to tylko możliwe.


3

W zależności od tego, co próbujesz zrobić, możesz również użyć curl_multi, aby to osiągnąć.



3

Możesz mieć opcję:

  1. multi_curl
  2. Można użyć do tego samego polecenia systemowego
  3. Idealny scenariusz to utworzenie funkcji wątkowania w języku C i kompilacja / konfiguracja w PHP. Teraz ta funkcja będzie funkcją PHP.


3

Co powiesz na pcntl_fork?

sprawdź na naszej stronie podręcznika przykłady: 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_forknie będzie działać w środowisku serwera WWW, jeśli ma włączony tryb awaryjny . W takim przypadku będzie działać tylko w wersji CLI PHP.


1

Jeśli korzystasz z serwera Linux, możesz użyć

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

Jeśli potrzebujesz przekazać kilka argumentów

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

W script.php

$args = $argv[1];

Lub użyj 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

W chwili pisania mojego obecnego komentarza nie wiem o wątkach PHP. Przyszedłem poszukać odpowiedzi tutaj, ale jednym obejściem jest to, że program PHP, który odbiera żądanie z serwera WWW, przekazuje całą formułę odpowiedzi do aplikacji konsolowej, która przechowuje dane wyjściowe, odpowiedź na żądanie, w pliku binarnym a program PHP, który uruchomił aplikację konsolową, zwraca ten plik binarny bajt po bajcie jako odpowiedź na otrzymane żądanie. Aplikację konsolową można napisać w dowolnym języku programowania działającym na serwerze, w tym w tych, które mają odpowiednią obsługę wątków, w tym w programach C ++ korzystających z OpenMP.

Jedną niewiarygodną, ​​brudną sztuczką jest użycie PHP do uruchomienia aplikacji konsolowej „uname”,

uname -a

i wydrukuj wynik tej komendy konsoli na wyjściu HTML, aby znaleźć dokładną wersję oprogramowania serwera. Następnie zainstaluj dokładnie tę samą wersję oprogramowania w instancji VirtualBox, skompiluj / zmontuj wszystko, co w pełni samodzielne, najlepiej statyczne, pliki binarne, które chcesz, a następnie prześlij je na serwer. Od tego momentu aplikacja PHP może używać tych plików binarnych w roli aplikacji konsoli, która ma właściwe wielowątkowość. Jest to brudne, niewiarygodne obejście problemu, gdy administrator serwera nie zainstalował na serwerze wszystkich potrzebnych implementacji języka programowania. Należy zwrócić uwagę na to, że przy każdym żądaniu otrzymania aplikacji PHP aplikacje konsolowe kończą działanie / exit / get_killed.

Co do tego, co administratorzy usług hostingowych myślą o takich wzorcach użytkowania serwera, myślę, że sprowadza się to do kultury. W Europie Północnej usługodawca MUSI DOSTARCZYĆ, CO ZOSTAŁO REKLAMOWANE, i jeśli zezwolono na wykonywanie poleceń konsoli i przesyłanie plików innych niż złośliwe oprogramowanie było dozwolone, a usługodawca ma prawo zabić dowolny proces serwera po kilku minutach, a nawet po 30 sekundach , wówczas administratorzy usług hostingowych nie mają żadnych argumentów za sformułowaniem właściwej skargi. W Stanach Zjednoczonych i Europie Zachodniej sytuacja / kultura jest bardzo różna i uważam, że istnieje duża szansa, że ​​w USA i / lub Europie Zachodniej dostawca usług hostingowych odmówi obsługi klientów usług hostingowych korzystających z wyżej opisanej sztuczki. To tylko moje przypuszczenie, biorąc pod uwagę moje osobiste doświadczenia z USA usługi hostingowe i biorąc pod uwagę to, co słyszałem od innych na temat usług hostingowych w Europie Zachodniej. W chwili pisania mojego obecnego komentarza (2018_09_01) nie wiem nic o normach kulturowych południowoeuropejskich dostawców usług hostingowych, południowoeuropejskich administratorów sieci.


-3

Być może coś mi umknęło, ale exec nie działało tak asynchronicznie dla mnie w środowisku Windows, którego użyłem w Windowsie i działało jak urok;)

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

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

1
Aby włamać się do wielowątkowości w PHP, powinieneś otworzyć wiele instrukcji popen () (PHP nie będzie na to czekać). Następnie po pół tuzinie otwartych wywołujesz na nich pclose () (PHP czeka na pclose (), aby zakończyć). W swoim kodzie zamykasz każdy wątek natychmiast po jego otwarciu, aby kod nie zachowywał się jak wielowątkowy.
Kmeixner,

-5

Wielowątkowość oznacza wykonywanie wielu zadań lub procesów jednocześnie, możemy to osiągnąć w php za pomocą następującego kodu, chociaż nie ma bezpośredniego sposobu na osiągnięcie wielowątkowości w php, ale możemy osiągnąć prawie takie same wyniki w następujący sposób.

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

}

Spowoduje to wykonanie testu_1.php dwa razy jednocześnie i oba procesy będą działały jednocześnie w tle, dzięki czemu można uzyskać wielowątkowość w php.

Ten facet wykonał naprawdę dobrą robotę Wielowątkowość w php


Nie ma to również nic wspólnego z MultiThreading. Jest to przetwarzanie równoległe. Zupełnie inne rzeczy.
Digital Human,

Moim zdaniem, jako obejście problemu, hack awaryjny, pomysł za oferowanym rozwiązaniem jest bardzo odpowiedni, ale sądzę, że różni ludzie mogą prowadzić wojny płomieniowe o to, co stanowi „prawdziwe wielowątkowość”, ponieważ istnieje różnica między współbieżnością a sprzętem przetwarzanie równoległe, jak opisano na stronie
Martin Vahi 1'18
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.