Comment utiliser le multi-threading dans les applications PHP


414

Existe-t-il un moyen réaliste d'implémenter un modèle multi-thread en PHP, que ce soit vraiment ou simplement en le simulant. Il y a quelque temps, il a é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 avec cela est que lorsque le code PHP a fini d'exécuter l'instance PHP reste en mémoire car il n'y a 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 faire du multi-threading ou de le simuler efficacement depuis PHP. Des idées?


1
Voir mes questions et réponses ici: stackoverflow.com/questions/2101640/…
powtac



Peut-être d'intérêt: pthreads.org
GibboK

Maintenant en 2020, il semble "parallèle" php.net/manual/en/intro.parallel.php est ce que nous voulons au lieu de "pthreads": stackoverflow.com/a/56451969/470749
Ryan

Réponses:


431

Le multi-threading est possible en php

Oui, vous pouvez faire du multi-threading en 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. Les applications PHP peuvent créer, lire, écrire, exécuter et synchroniser avec les objets Threads, Workers et Threaded.

Avertissement : L'extension pthreads ne peut pas être utilisée dans un environnement de serveur Web. Le threading en PHP devrait donc rester réservé 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);
}

3
@Baba, je ne peux pas configurer et installer pthreads sur le serveur Xampp. Pouvez-vous m'aider?
Irfan

4
Téléchargez le binaire Windows ici windows.php.net/downloads/pecl/releases/pthreads/0.0.45
Baba

17
C'est bien, je n'ai pas touché PHP depuis des années et maintenant il a des capacités de multithreading!
cruizer

1
Agréable et simple! Pour info, je déploie une application sur le serveur Azure Cloud Win et si seule la configuration de base à 1 cœur est sélectionnée, le multithread ne sera pas disponible à moins que davantage de cœurs ne soient ajoutés.
Milan

3
Attention: Joe Watkins, l'auteur de l'extension pthreads a interrompu le développement en faveur de la nouvelle extension parallèle: github.com/krakjoe/pthreads/issues/929
Anton Belonovich

42

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

4
J'utilise la solution ci-dessus et fonctionne très bien, je pense que c'était le moyen le plus simple de faire un processus parallèle en utilisant php.
GodFather

7
comme @ e-info128 l'a dit, cette implémentation bifurque le processus, ce qui signifie qu'il s'exécute sur un processus différent et ne partage pas les ressources du processus. Cela étant dit, si le travail à effectuer n'a pas besoin de partager les ressources, cela fonctionnera toujours et se déroulera en parallèle.
Raffi

1
Comment transmettre des variables à popen sans utiliser de variables de session?
atwellpub

@atwellpub Pas question, ce sont des processus séparés ne partageant aucune ressource. Même les sessions seront un mécanisme IPC maladroit
Cholthi Paul Ttiopic

1
Pour leur transmettre des données, vous pouvez également utiliser des arguments et le serveur Redis.
Amir Fo

21

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

Avec le paramètre de délai d'attente de la boucle défini sur 1 et en utilisant le même identifiant de session pour les processus que vous souhaitez associer les uns aux autres, vous pouvez communiquer avec des variables de session comme dans mon 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 l'ID de session correct comme ceci:

http: //localhost/test/verifysession.php? sessionid = [l' identifiant 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);

4
La dernière fois que j'ai vérifié (il y a quelques années), php ne permettait pas d'accéder au stockage de session basé sur des fichiers par deux processus simultanément. Il verrouille le fichier et le deuxième processus doit rester là en attendant que le premier script s'arrête. Je parle de l'environnement du serveur Web, pas du CLI.
Alexei Tenitski

5
set_time_limit(0);beurk! Ne fais jamais ça.
Kafoso

@Kafoso Kafoso pourquoi pas? Eh bien, je suis d'accord pour PHP en tant que processeur de script Web, mais pourquoi pas en CLI? Si quelque chose ne va pas, CLI peut être tué avec Ctrl + C ...
sijanec

14

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 bifurquer votre processus avec pcntl_fork - renvoyant le PID de l'enfant. Ensuite, vous pouvez utiliser posix_kill pour supprimer ce PID.

Cela dit, si vous tuez un processus parent, un signal doit être envoyé au processus enfant lui disant de mourir. Si php lui-même ne le reconnaît pas, vous pouvez enregistrer une fonction pour le gérer et faire une sortie propre en utilisant pcntl_signal.


1
Cette réponse est désormais très dépassée (ce qui est très juste sachant qu'elle a 11 ans). Regardez les pthreads ci-dessous.
Maciej Paprocki

@MaciejPaprocki pThread est maintenant interrompu à partir de php 7.4 au lieu d'utiliser parallèle
Airy


10

Je sais que c'est une vieille question mais pour les personnes qui recherchent, il y a une extension PECL écrite en C qui donne maintenant la capacité de multi-threading PHP, elle est située ici https://github.com/krakjoe/pthreads


pThread est maintenant interrompu à partir de php 7.4 au lieu d'utiliser parallèle
Airy

5

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

Je ne me souviens pas vraiment de la syntaxe php 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 désactivé exec () par défaut pour des raisons de sécurité, mais cela pourrait valoir la peine d'essayer.


4

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


3

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


3

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

Il prend en charge la communication bidirectionnelle entre les threads et dispose également de protections intégrées pour éliminer les threads enfants (empêcher les orphelins).


3

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 threading en langage C et de compiler / configurer en PHP. Maintenant, cette fonction sera la fonction de PHP.

3

La classe Thread est disponible depuis les pthreads PECL ≥ 2.0.0.


1
puis-je exécuter un thread par HTTP? simple: votrenom.com/thread.php?
Mon nom

pThread est maintenant interrompu à partir de php 7.4 au lieu d'utiliser parallèle
Airy

3

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
    }

?>

2

pcntl_forkne fonctionnera pas dans un environnement de serveur Web si le mode sans échec est activé. Dans ce cas, cela ne fonctionnera que dans la version CLI de PHP.


1

Si vous utilisez un serveur Linux, vous pouvez utiliser

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

Si vous avez besoin de passer quelques arguments

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

Dans script.php

$args = $argv[1];

Ou utilisez 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

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 demande, à 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 ont une prise en charge de thread appropriée, y compris les programmes C ++ qui utilisent 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 dans la sortie HTML pour connaître la version exacte du logiciel serveur. Ensuite, installez la même version exacte du logiciel sur une instance de VirtualBox, compilez / assemblez les binaires entièrement autonomes, de préférence statiques, que vous souhaitez, puis télé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 qui a le multi-threading approprié. C'est une solution de contournement sale et peu fiable à 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 console se termine / exit / get_killed.

Quant à 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 LIVRER CE QUI A ÉTÉ ANNONCÉ et si l'exécution des commandes de la console a été autorisée et le téléchargement de fichiers non malveillants a été 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 , alors les administrateurs du service d'hébergement n'ont aucun argument pour former une plainte appropriée. Aux États-Unis et en Europe occidentale, la situation / culture est très différente et je pense qu'il y a de grandes chances qu'aux États-Unis et / ou en Europe occidentale, le fournisseur de services d'hébergement refuse de servir les clients de services d'hébergement qui utilisent l'astuce décrite ci-dessus. C'est juste ma supposition, étant donné mon expérience personnelle avec les États-Unis services d'hébergement et compte tenu de ce que j'ai entendu d'autres sur les 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 hébergeurs sud-européens, administrateurs de réseaux sud-européens.


-3

Peut-être que j'ai raté quelque chose, mais l'exec n'a pas fonctionné comme asynchrone pour moi dans l'environnement Windows que j'ai utilisé dans Windows et cela a fonctionné comme un charme;)

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

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

1
Pour pirater le multithreading en PHP, vous devez ouvrir plusieurs instructions popen () (PHP n'attendra pas qu'il soit terminé). Ensuite, une demi-douzaine environ sont ouverts, vous appelez pclose () sur ceux-ci (PHP attendra sur pclose () pour terminer). Dans votre code, vous fermez chaque thread immédiatement après l'avoir ouvert afin que votre code ne se comporte pas comme multithread.
Kmeixner

-5

Le multithreading signifie effectuer plusieurs tâches ou processus simultanément, nous pouvons y parvenir en php en utilisant le code suivant, bien qu'il n'y ait pas de moyen direct de réaliser le multithreading en php mais nous pouvons obtenir presque les mêmes résultats en suivant le chemin.

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

}

Cela exécutera test_1.php deux fois simultanément et les deux processus s'exécuteront en arrière-plan simultanément, de cette façon, vous pouvez réaliser le multithreading en php.

Ce gars a fait du très bon travail Multithreading en php


De plus, cela n'a rien à faire avec MultiThreading. Il s'agit d'un traitement parallèle. Des choses totalement différentes.
Digital Human

À mon avis, en tant que solution de contournement, un hack d'urgence, l'idée derrière la solution proposée est très appropriée, mais je suppose que différentes personnes peuvent avoir des guerres enflammées sur ce qui constitue le "vrai multi-threading", car il existe une distinction entre la concurrence et le matériel. traitement parallèle, comme décrit sur: youtube.com/watch?v=cN_DpYBzKso
Martin Vahi
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.