web-dev-qa-db-fra.com

Comment utiliser le multi-threading dans les applications PHP

Existe-t-il un moyen réaliste d’implémenter un modèle multithread dans PHP que ce soit réellement ou simplement en le simulant? Il y a quelque temps, il avait été suggéré de forcer le système d'exploitation à charger une autre instance de l'exécutable PHP et à gérer d'autres processus simultanés.

Le problème, c’est que lorsque le code PHP a fini d’exécuter l’instance PHP, il reste en mémoire car il n’ya aucun moyen de le tuer depuis PHP. Donc, si vous simulez plusieurs threads, vous pouvez imaginer ce qui va se passer. Je suis donc toujours à la recherche d'un moyen de créer ou de simuler efficacement le multi-threading depuis PHP. Des idées?

374
Steve Obbayi

Le multi-threading est possible en php

Oui, vous pouvez faire du multi-threading dans PHP avec pthreads

De la documentation PHP :

pthreads est une API orientée objet qui fournit tous les outils nécessaires au multi-threading en PHP. PHP les applications peuvent créer, lire, écrire, exécuter et se synchroniser avec les objets Threads, Workers et Threaded.

Avertissement : l'extension pthreads ne peut pas être utilisée dans un environnement de serveur Web. Les threads dans PHP doivent donc rester réservés aux applications CLI.

Test simple

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

?>

Première exécution

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

Deuxième manche

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

Exemple du monde réel

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

pourquoi n'utilisez-vous pas 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]);
    }
}
37
masterb

Le threading n'est pas disponible en stock en PHP, mais la programmation simultanée est possible en utilisant des requêtes HTTP comme appels asynchrones.

Avec le paramètre de délai d'expiration de curl défini sur 1 et en utilisant le même id_session pour les processus que vous souhaitez associer les uns aux autres, vous pouvez communiquer avec les variables de session comme dans l'exemple ci-dessous. Avec cette méthode, vous pouvez même fermer votre navigateur et le processus simultané existe toujours sur le serveur.

N'oubliez pas de vérifier le bon identifiant de session comme ceci:

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

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

Bien que vous ne puissiez pas enfiler, vous avez un certain degré de contrôle de processus en php. Les deux jeux de fonctions utiles ici sont:

Fonctions de contrôle de processus http://www.php.net/manual/en/ref.pcntl.php

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

Vous pouvez lancer votre processus avec pcntl_fork - renvoyer le PID de l'enfant. Ensuite, vous pouvez utiliser posix_kill pour décrire ce PID.

Cela dit, si vous supprimez un processus parent, un signal doit être envoyé au processus enfant lui indiquant de mourir. Si php lui-même ne le reconnaît pas, vous pouvez enregistrer une fonction pour le gérer et effectuer une sortie propre à l'aide de pcntl_signal.

11
J.D. Fitz.Gerald

l’utilisation de threads est rendue possible par l’extension PECL de pthreads

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

10
pinkal vansia

Je sais que c’est une vieille question, mais pour les personnes en quête de recherche, il existe une extension PECL écrite en C qui donne à PHP la capacité de multi-threading à présent, elle se trouve ici https://github.com/ krakjoe/pthreads

9
JasonDavis

Vous pouvez utiliser exec () pour exécuter un script de ligne de commande (tel que php en ligne de commande). Si vous dirigez la sortie vers un fichier, votre script n'attendra pas la fin de la commande.

Je ne me souviens plus très bien de la syntaxe php de la CLI, mais vous voudriez quelque chose comme:

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

Je pense que bon nombre de serveurs d'hébergement partagé ont exec () désactivé par défaut pour des raisons de sécurité, mais cela vaut peut-être la peine d'essayer.

6
Adam Hopkinson

Vous pouvez simuler le threading. PHP peut exécuter des processus en arrière-plan via popen (ou proc_open). Ces processus peuvent être communiqués via stdin et stdout. Bien sûr, ces processus peuvent être eux-mêmes un programme php. C'est probablement aussi proche que vous obtiendrez.

5
Pete

Que diriez-vous de pcntl_fork?

consultez notre page de manuel pour des exemples: 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
    }

?>
4
Jarrod

En fonction de ce que vous essayez de faire, vous pouvez également utiliser curl_multi pour y parvenir.

3
Sheldmandu

Je sais que c'est bien vieux, mais vous pourriez regarder http://phpthreadlib.sourceforge.net/

Il prend en charge la communication bidirectionnelle entre threads et intègre également des protections permettant de supprimer les threads enfants (prévention des orphelins).

3
Unsigned

Le classe de thread est disponible depuis les pthreads PECL ≥ 2.0.0.

3
Dhananjay Kashyap

pcntl_fork ne fonctionnera pas dans un environnement de serveur Web s'il a mode sans échec activé. Dans ce cas, cela ne fonctionnera que dans la version CLI de PHP.

2
Stilero

Vous pouvez avoir l'option de:

  1. multi_curl
  2. On peut utiliser la commande système pour le même
  3. Le scénario idéal est de créer une fonction de thread en langage C et de compiler/configurer en PHP. Maintenant, cette fonction sera la fonction de PHP.
2
Manoj Donga

Au moment de la rédaction de mon commentaire actuel, je ne connaissais pas les threads PHP. Je suis venu chercher la réponse ici moi-même, mais une solution de contournement est que le programme PHP qui reçoit la demande du serveur Web délègue toute la formulation de la réponse à une application console qui stocke sa sortie, la réponse à la request, dans un fichier binaire et le programme PHP qui a lancé l'application console, renvoie ce fichier binaire octet par octet comme réponse à la demande reçue. L'application console peut être écrite dans n'importe quel langage de programmation qui s'exécute sur le serveur, y compris ceux qui sont correctement pris en charge par les threads, y compris les programmes C++ utilisant OpenMP.

Une astuce peu fiable et sale consiste à utiliser PHP pour exécuter une application console, "uname",

uname -a

et imprimez la sortie de cette commande de console sur la sortie HTML pour connaître la version exacte du logiciel serveur. Installez ensuite la même version du logiciel sur une instance VirtualBox, compilez/assemblez les fichiers binaires entièrement autonomes, de préférence statiques, souhaités, puis chargez-les sur le serveur. À partir de ce moment, l'application PHP peut utiliser ces fichiers binaires dans le rôle de l'application console dotée du multitâche approprié. Il s'agit d'une solution de contournement peu fiable et peu fiable dans une situation où l'administrateur du serveur n'a pas installé toutes les implémentations de langage de programmation nécessaires sur le serveur. La chose à surveiller est qu'à chaque demande que l'application PHP reçoive la ou les applications de la console, elle se termine/exit/get_killed.

En ce qui concerne ce que les administrateurs de services d’hébergement pensent de ces modèles d’utilisation du serveur, je suppose que cela se résume à la culture. En Europe du Nord, le fournisseur de services DOIT FOURNIR CE QUI A ÉTÉ PUBLIÉ. Si l'exécution des commandes de la console était autorisée, le téléchargement de fichiers autres que de logiciels malveillants était autorisé et le fournisseur de services a le droit de tuer tout processus de serveur après quelques minutes ou même après 30 secondes. , les administrateurs du service d’hébergement ne disposent alors d’aucun argument en faveur d’une plainte appropriée. Aux États-Unis et en Europe occidentale, la situation/la culture est très différente et je pense qu’il ya de grandes chances que, aux États-Unis et/ou en Europe occidentale, le fournisseur de service d’hébergement refuse de servir les clients du service d’hébergement qui utilisent l’astuce décrite ci-dessus. C’est ce que je suppose, compte tenu de mon expérience personnelle avec les services d’hébergement américains et de ce que j’ai entendu dire par d’autres personnes au sujet des services d’hébergement en Europe occidentale. Au moment de la rédaction de mon commentaire actuel (2018_09_01), je ne connaissais rien aux normes culturelles des fournisseurs de services d'hébergement du sud de l'Europe, les administrateurs de réseau du sud de l'Europe.

0
Martin Vahi