Wie kann man Multithreading in PHP-Anwendungen verwenden?

414

Gibt es eine realistische Möglichkeit, ein Multithread-Modell in PHP wirklich zu implementieren oder nur zu simulieren? Vor einiger Zeit wurde vorgeschlagen, das Betriebssystem zu zwingen, eine andere Instanz der ausführbaren PHP-Datei zu laden und andere gleichzeitige Prozesse zu verarbeiten.

Das Problem dabei ist, dass die PHP-Instanz nach Abschluss der Ausführung des PHP-Codes im Speicher verbleibt, da es keine Möglichkeit gibt, sie innerhalb von PHP zu beenden. Wenn Sie also mehrere Threads simulieren, können Sie sich vorstellen, was passieren wird. Daher suche ich immer noch nach einer Möglichkeit, wie Multithreading in PHP effektiv durchgeführt oder simuliert werden kann. Irgendwelche Ideen?

Steve Obbayi
quelle
1
Siehe meine Frage und Antworten hier: stackoverflow.com/questions/2101640/…
powtac
1
... und meins hier: stackoverflow.com/questions/209774/does-php-have-threading/…
Francois Bourgeois
Verwendung der pthreads-Erweiterung: phplobby.com/php-multi-thread-on-windows-pthreads-configuration
Emrah Mehmedov
Vielleicht von Interesse: pthreads.org
GibboK
Jetzt im Jahr 2020 scheint es "parallel" zu sein, php.net/manual/en/intro.parallel.php ist das, was wir wollen, anstelle von "pthreads": stackoverflow.com/a/56451969/470749
Ryan

Antworten:

431

Multithreading ist in PHP möglich

Ja, Sie können Multithreading in PHP mit Pthreads durchführen

Aus der PHP-Dokumentation :

pthreads ist eine objektorientierte API, die alle für das Multithreading in PHP erforderlichen Tools bereitstellt. PHP-Anwendungen können Threads, Worker und Threaded-Objekte erstellen, lesen, schreiben, ausführen und synchronisieren.

Warnung : Die Erweiterung pthreads kann in einer Webserverumgebung nicht verwendet werden. Das Threading in PHP sollte daher nur für CLI-basierte Anwendungen gelten.

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

?>

Erster Lauf

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

Zweiter Lauf

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

Beispiel aus der realen Welt

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
quelle
3
@Baba, ich kann pthreads nicht auf dem Xampp-Server konfigurieren und installieren. Kannst du mir damit helfen?
Irfan
4
Laden Sie Windows Binary hier herunter windows.php.net/downloads/pecl/releases/pthreads/0.0.45
Baba
17
Das ist schön, ich habe PHP seit Jahren nicht mehr berührt und jetzt hat es Multithreading-Funktionen!
Kreuzer
1
Schön und einfach! Nur zu Ihrer Information, ich stelle eine App auf dem Azure Cloud Win-Server bereit. Wenn nur die grundlegende 1-Kernkonfiguration ausgewählt ist, ist das Multithreading nur verfügbar, wenn weitere Kerne hinzugefügt werden.
Mailand
3
Bitte beachten Sie: Joe Watkins, der Autor der pthreads-Erweiterung, hat die Entwicklung zugunsten der neuen parallelen Erweiterung eingestellt: github.com/krakjoe/pthreads/issues/929
Anton Belonovich
42

Warum benutzt du nicht 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]);
    }
}
masterb
quelle
4
Ich verwende die obige Lösung und funktioniert einwandfrei. Ich denke, dass dies der einfachste Weg war, um parallele Prozesse mit PHP durchzuführen.
GodFather
7
Wie @ e-info128 sagte, verzweigt diese Implementierung den Prozess, was bedeutet, dass er auf einem anderen Prozess ausgeführt wird und keine Prozessressourcen gemeinsam nutzt. Wenn der vorliegende Job jedoch keine Ressourcen gemeinsam nutzen muss, funktioniert dies weiterhin und wird parallel ausgeführt.
Raffi
1
Wie würden Sie Variablen an Popen übergeben, ohne Sitzungsvariablen zu verwenden?
Atwellpub
@atwellpub Auf keinen Fall sind dies separate Prozesse, die keine Ressourcen gemeinsam nutzen. Sogar Sitzungen werden umständliche IPC-Mechanismen sein
Cholthi Paul Ttiopic
1
Um Daten an sie zu übergeben, können Sie auch Argumente und den Redis-Server verwenden.
Amir Fo
21

Threading ist in Standard-PHP nicht verfügbar, aber die gleichzeitige Programmierung ist möglich, indem HTTP-Anforderungen als asynchrone Aufrufe verwendet werden.

Wenn die Zeitlimiteinstellung der Locke auf 1 gesetzt ist und dieselbe Sitzungs-ID für die Prozesse verwendet wird, die Sie miteinander verknüpfen möchten, können Sie mit Sitzungsvariablen wie in meinem Beispiel unten kommunizieren. Mit dieser Methode können Sie sogar Ihren Browser schließen und der gleichzeitige Prozess ist noch auf dem Server vorhanden.

Vergessen Sie nicht, die richtige Sitzungs-ID wie folgt zu überprüfen:

http: //localhost/test/verifysession.php? sessionid = [die richtige 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);
Ricardo
quelle
4
Als ich das letzte Mal (vor einigen Jahren) nachgesehen habe, hat PHP nicht zugelassen, dass zwei Prozesse gleichzeitig auf dateibasierten Sitzungsspeicher zugreifen. Es sperrt die Datei und der zweite Prozess muss dort warten, bis das erste Skript beendet wird. Ich spreche von einer Webserver-Umgebung, nicht von CLI.
Alexei Tenitski
5
set_time_limit(0);Huch! Mach das niemals.
Kafoso
@ Kafoso Kafoso warum nicht? Nun, ich stimme PHP als Web-Skript-Prozessor zu, aber warum nicht in CLI? Wenn etwas schief geht, kann CLI mit Strg + C ...
sijanec
14

Während Sie nicht fädeln können, haben Sie ein gewisses Maß an Prozesskontrolle in PHP. Die beiden hier nützlichen Funktionssätze sind:

Prozesssteuerungsfunktionen http://www.php.net/manual/en/ref.pcntl.php

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

Sie können Ihren Prozess mit pcntl_fork verzweigen und die PID des Kindes zurückgeben. Dann können Sie posix_kill verwenden, um diese PID zu entsorgen.

Das heißt, wenn Sie einen übergeordneten Prozess beenden, sollte ein Signal an den untergeordneten Prozess gesendet werden, das ihn zum Sterben auffordert. Wenn PHP dies nicht erkennt, können Sie eine Funktion registrieren, um es zu verwalten und einen sauberen Exit mit pcntl_signal durchzuführen.

JD Fitz.Gerald
quelle
1
Diese Antwort ist jetzt sehr veraltet (was sehr fair ist, wenn man weiß, dass sie 11 Jahre alt ist). Schauen Sie sich die pthreads unten an.
Maciej Paprocki
@MaciejPaprocki pThread wird jetzt von PHP 7.4 eingestellt und stattdessen parallel verwendet
Airy
10

Ich weiß, dass dies eine alte Frage ist, aber für Leute, die suchen, gibt es eine in C geschriebene PECL-Erweiterung, die jetzt PHP-Multithreading-Funktionen bietet. Sie befindet sich hier https://github.com/krakjoe/pthreads

JasonDavis
quelle
pThread wird jetzt von PHP 7.4 eingestellt und stattdessen parallel verwendet
Airy
5

Sie können exec () verwenden, um ein Befehlszeilenskript (z. B. Befehlszeilen-PHP) auszuführen. Wenn Sie die Ausgabe an eine Datei weiterleiten, wartet Ihr Skript nicht auf den Abschluss des Befehls.

Ich kann mich nicht ganz an die PHP-CLI-Syntax erinnern, aber Sie möchten etwas wie:

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

Ich denke, einige Shared Hosting Server haben exec () aus Sicherheitsgründen standardmäßig deaktiviert, aber es könnte einen Versuch wert sein.

Adam Hopkinson
quelle
4

Sie könnten das Einfädeln simulieren. PHP kann Hintergrundprozesse über popen (oder proc_open) ausführen. Diese Prozesse können über stdin und stdout kommuniziert werden. Natürlich können diese Prozesse selbst ein PHP-Programm sein. Das ist wahrscheinlich so nah wie möglich.

Pete
quelle
3

Je nachdem, was Sie versuchen, können Sie auch curl_multi verwenden, um dies zu erreichen.

Sheldmandu
quelle
3

Ich weiß, dass dies viel alt ist, aber Sie können sich http://phpthreadlib.sourceforge.net/ ansehen.

Es unterstützt die bidirektionale Kommunikation zwischen Threads und verfügt über integrierte Schutzfunktionen zum Abtöten von untergeordneten Threads (Verhinderung von Waisen).

Ohne Vorzeichen
quelle
3

Sie haben die Wahl zwischen:

  1. multi_curl
  2. Man kann dafür den Systembefehl verwenden
  3. Ideales Szenario ist, eine Threading-Funktion in C-Sprache zu erstellen und in PHP zu kompilieren / konfigurieren. Diese Funktion wird nun die Funktion von PHP sein.
Manoj Donga
quelle
3

Die Thread-Klasse ist verfügbar, da PECL-Pthreads ≥ 2.0.0 sind.

Dhananjay Kashyap
quelle
1
Kann ich Thread über HTTP ausführen? einfach: yourname.com/thread.php?
Mein Name
pThread wird jetzt von PHP 7.4 eingestellt und stattdessen parallel verwendet
Airy
3

Wie wäre es mit pcntl_fork?

Beispiele finden Sie auf der Handbuchseite: 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
    }

?>
Jarrod
quelle
2

pcntl_forkfunktioniert in einer Webserverumgebung nicht, wenn der abgesicherte Modus aktiviert ist. In diesem Fall funktioniert es nur in der CLI-Version von PHP.

Stilero
quelle
1

Wenn Sie einen Linux-Server verwenden, können Sie verwenden

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

Wenn Sie einige Argumente übergeben müssen

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

In script.php

$args = $argv[1];

Oder verwenden Sie Symfony https://symfony.com/doc/current/components/process.html

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

Zum Zeitpunkt des Schreibens meines aktuellen Kommentars weiß ich nichts über die PHP-Threads. Ich habe hier selbst nach der Antwort gesucht, aber eine Problemumgehung besteht darin, dass das PHP-Programm, das die Anforderung vom Webserver empfängt, die gesamte Antwortformulierung an eine Konsolenanwendung delegiert, die ihre Ausgabe, die Antwort auf die Anforderung, in einer Binärdatei speichert und das PHP-Programm, das die Konsolenanwendung gestartet hat, gibt diese Binärdatei Byte für Byte als Antwort auf die empfangene Anforderung zurück. Die Konsolenanwendung kann in jeder Programmiersprache geschrieben werden, die auf dem Server ausgeführt wird, einschließlich solcher, die eine ordnungsgemäße Threading-Unterstützung bieten, einschließlich C ++ - Programmen, die OpenMP verwenden.

Ein unzuverlässiger, schmutziger Trick besteht darin, PHP zum Ausführen einer Konsolenanwendung zu verwenden.

uname -a

und drucken Sie die Ausgabe dieses Konsolenbefehls in die HTML-Ausgabe, um die genaue Version der Serversoftware zu ermitteln. Installieren Sie dann genau dieselbe Version der Software auf einer VirtualBox-Instanz, kompilieren / montieren Sie alle vollständig eigenständigen, vorzugsweise statischen Binärdateien, die Sie möchten, und laden Sie diese dann auf den Server hoch. Ab diesem Zeitpunkt kann die PHP-Anwendung diese Binärdateien in der Rolle der Konsolenanwendung verwenden, die über ein ordnungsgemäßes Multithreading verfügt. Es ist eine schmutzige, unzuverlässige Problemumgehung für eine Situation, in der der Serveradministrator nicht alle erforderlichen Programmiersprachenimplementierungen auf dem Server installiert hat. Achten Sie darauf, dass bei jeder Anforderung, dass die PHP-Anwendung die Konsolenanwendung (en) empfängt, / exit / get_killed beendet wird.

Was die Hosting-Service-Administratoren von solchen Servernutzungsmustern halten, hängt wohl von der Kultur ab. In Nordeuropa MUSS der Dienstanbieter LIEFERN, WAS ANGEZEIGT WURDE und ob die Ausführung von Konsolenbefehlen und das Hochladen von Nicht-Malware-Dateien zulässig war und der Dienstanbieter das Recht hat, jeden Serverprozess nach einigen Minuten oder sogar nach 30 Sekunden abzubrechen Dann fehlen den Hosting-Service-Administratoren Argumente für eine ordnungsgemäße Beschwerde. In den USA und Westeuropa ist die Situation / Kultur sehr unterschiedlich, und ich glaube, dass es eine große Chance gibt, dass sich der Hosting-Dienstleister in den USA und / oder Westeuropa weigert, Hosting-Service-Kunden zu bedienen, die den oben beschriebenen Trick anwenden. Das ist nur meine Vermutung angesichts meiner persönlichen Erfahrung mit den USA Hosting-Dienste und gegeben, was ich von anderen über westeuropäische Hosting-Dienste gehört habe. Zum Zeitpunkt des Schreibens meines aktuellen Kommentars (2018_09_01) weiß ich nichts über die kulturellen Normen der südeuropäischen Hosting-Dienstleister, südeuropäischen Netzwerkadministratoren.

Martin Vahi
quelle
-3

Vielleicht habe ich etwas verpasst, aber exec hat in der Windows-Umgebung, die ich in Windows verwendet habe, nicht so asynchron für mich funktioniert und es hat wie ein Zauber funktioniert;)

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

pclose(popen("start /B ". $script_exec, "r"));
Mubashar
quelle
1
Um Multithreading in PHP zu hacken, sollten Sie mehrere popen () - Anweisungen öffnen (PHP wartet nicht darauf, dass es abgeschlossen ist). Dann, nachdem ungefähr ein halbes Dutzend geöffnet sind, rufen Sie pclose () auf diesen auf (PHP wartet, bis pclose () abgeschlossen ist). In Ihrem Code schließen Sie jeden Thread sofort nach dem Öffnen, damit sich Ihr Code nicht wie Multithreading verhält.
Kmeixner
-5

Multithreading bedeutet, mehrere Aufgaben oder Prozesse gleichzeitig auszuführen. Wir können dies in PHP erreichen, indem wir folgenden Code verwenden. Es gibt zwar keinen direkten Weg, um Multithreading in PHP zu erreichen, aber wir können fast dieselben Ergebnisse erzielen, indem wir den folgenden Weg gehen.

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

}

Dadurch wird test_1.php zweimal gleichzeitig ausgeführt und beide Prozesse werden gleichzeitig im Hintergrund ausgeführt. Auf diese Weise können Sie Multithreading in PHP erreichen.

Dieser Typ hat wirklich gute Arbeit geleistet Multithreading in PHP

Pir Abdul
quelle
Dies hat auch nichts mit MultiThreading zu tun. Dies ist eine Parallelverarbeitung. Ganz andere Dinge.
Digital Human
Meiner Meinung nach ist die Idee hinter der angebotenen Lösung als Problemumgehung, als Notfall-Hack sehr angemessen, aber ich denke, dass verschiedene Leute Flammenkriege darüber führen können, was "echtes Multithreading" ausmacht, da zwischen Parallelität und Hardware unterschieden wird parallele Verarbeitung, wie unter youtube.com/watch?v=cN_DpYBzKso beschrieben
Martin Vahi