web-dev-qa-db-fra.com

Est-ce que PHP a des threads?

J'ai trouvé ceci paquet PECL appelé threads , mais il n'y a pas encore de version. Et rien ne se passe sur le site Web PHP.

125
Thomas Owens

Il n'y a rien de disponible à ma connaissance. La meilleure chose à faire serait simplement de faire en sorte qu'un script en exécute un autre via la CLI, mais c'est un peu rudimentaire. Selon ce que vous essayez de faire et sa complexité, cela peut être une option ou non.

40
Wilco

Extrait du manuel PHP pour les pthreads :

pthreads est une API orientée objet qui permet le multi-threading en langage utilisateur en PHP. Il comprend tous les outils dont vous avez besoin pour créer des applications multithreads destinées au Web ou à la console. PHP les applications peuvent créer, lire, écrire, exécuter et se synchroniser avec Threads, Workers et Stackables.

Aussi incroyable que cela puisse paraître, c'est tout à fait vrai. Aujourd'hui, PHP peut gérer plusieurs threads pour ceux qui souhaitent l'essayer.

La première version de PHP4, le 22 mai 2000, PHP était livrée avec une architecture thread-safe, ce qui lui permettait d'exécuter plusieurs instances de son interpréteur dans des threads distincts dans des environnements SAPI (API de serveur) à plusieurs threads. Au cours des 13 dernières années, la conception de cette architecture a été maintenue et avancée: elle est depuis utilisée en production sur les plus grands sites Web du monde.

Le threading dans le pays utilisateur n'a jamais été une préoccupation pour l'équipe PHP, et il le reste aujourd'hui. Vous devez comprendre que dans le monde où PHP est actif, il existe déjà une méthode de dimensionnement définie - ajouter du matériel. Au cours des nombreuses années PHP, le matériel est devenu de moins en moins cher, ce qui est devenu une préoccupation de plus en plus préoccupante pour l'équipe PHP. Alors que cela devenait moins cher, cela devenait aussi beaucoup plus puissant; Aujourd'hui, nos téléphones mobiles et nos tablettes ont des architectures à deux et quatre cœurs et beaucoup de RAM, nos ordinateurs de bureau et serveurs ont généralement 8 ou 16 cœurs, 16 et 32 ​​gigaoctets de RAM, bien que nous ne puissions pas pouvoir toujours avoir deux dans le budget et avoir deux bureaux est rarement utile pour la plupart d'entre nous.

De plus, PHP a été écrit pour les non-programmeurs, il s'agit de la langue maternelle de nombreux amateurs. La raison pour laquelle PHP est si facilement adoptée est qu’il s’agit d’un langage facile à apprendre et à écrire. La raison pour laquelle PHP est si fiable aujourd'hui est due à la quantité de travail nécessaire à sa conception, et à toutes les décisions prises par le groupe PHP. Sa fiabilité et sa grandeur la mettent sous les projecteurs, après toutes ces années; où ses rivaux sont tombés au temps ou à la pression.

La programmation multi-thread n'est pas facile pour la plupart, même avec les API les plus cohérentes et les plus fiables, il y a différentes choses à penser et de nombreuses idées fausses. Le groupe PHP ne souhaite pas que le multi-thread utilisateur Land soit une fonctionnalité essentielle, il n'a jamais fait l'objet d'une attention sérieuse - et à juste titre. PHP ne devrait pas être complexe, pour tout le monde.

Tout bien considéré, il est toujours avantageux de permettre à PHP d’utiliser ses fonctionnalités prêtes pour la production et testées afin de permettre de tirer le meilleur parti de ce que nous avons, quand en ajouter plus n’est pas toujours une priorité. option, et pour beaucoup de tâches n’est jamais vraiment nécessaire.

pthreads réalise, pour ceux qui souhaitent l'explorer, une API qui permet à un utilisateur de créer plusieurs applications PHP. Son API est un travail en cours, désignant un niveau bêta de stabilité et d’exhaustivité.

Il est de notoriété publique que certaines des bibliothèques PHP utilisées ne sont pas thread-safe, le programmeur devrait bien comprendre que pthreads ne peut pas changer cela et ne tente pas d'essayer. Cependant, toute bibliothèque qui est thread-safe est utilisable, comme dans toute autre configuration thread-safe de l'interpréteur.

pthreads utilise Posix Threads (même dans Windows), ce que le programmeur crée sont de vrais threads d’exécution, mais pour que ces threads soient utiles, ils doivent connaître PHP - en mesure d’exécuter du code utilisateur, des variables de partage et permettre un moyen de communication utile (synchronisation). Ainsi, chaque thread est créé avec une instance de l'interpréteur, mais, de par sa conception, son interpréteur est isolé de toutes les autres instances de l'interpréteur, à l'instar des environnements API de serveur multithreads. pthreads tente de combler le fossé de manière saine et sûre. Beaucoup de soucis du programmeur de threads en C ne sont tout simplement pas là pour le programmeur de pthreads, par conception, pthreads est une copie en lecture et une copie en écriture (RAM est bon marché), donc pas deux instances jamais manipuler les mêmes données physiques, mais ils peuvent tous deux affecter les données d’un autre thread. Le fait que PHP puisse utiliser des fonctionnalités non sécurisées pour les threads dans sa programmation principale est totalement dépourvu de pertinence, les threads utilisateur et ses opérations sont totalement sécurisés.

Pourquoi copier en lecture et copie en écriture:

public function run() {
    ...
    (1) $this->data = $data;
    ...
    (2) $this->other = someOperation($this->data);
    ...
}

(3) echo preg_match($pattern, $replace, $thread->data);

(1) Lorsqu'un verrou en lecture et en écriture est maintenu sur le magasin de données d'objet pthreads, les données sont copiées de leur emplacement d'origine en mémoire dans le magasin d'objets. pthreads ne modifie pas le nombre de références de la variable, Zend peut libérer les données d'origine s'il n'y a pas d'autres références.

(2) L'argument de someOperation fait référence à la librairie, les données d'origine stockées, qui sont elles-mêmes une copie du résultat de (1), sont à nouveau copiées pour le moteur dans un conteneur zval. Un verrouillage de lecture est alors maintenu enfoncé. le magasin d'objets, le verrou est libéré et le moteur peut exécuter la fonction. Lorsque le zval est créé, son nombre de références est 0, ce qui permet au moteur de libérer la copie à la fin de l'opération, car il n'existe aucune autre référence à celle-ci.

(3) Le dernier argument de preg_match fait référence au magasin de données, un verrou de lecture est obtenu, le jeu de données dans (1) est copié dans un zval, toujours avec un compteur de référence de 0. Le verrou est libéré, l'appel à preg_match fonctionne une copie des données, qui est elle-même une copie des données originales.

Ce qu'il faut savoir:

  • La table de hachage de la librairie dans laquelle les données sont stockées, thread-safe, est
    basé sur le TsHashTable livré avec PHP, par Zend.

  • La bibliothèque d'objets a un verrou en lecture et en écriture, un verrou d'accès supplémentaire est fourni pour la table TsHashTable, de sorte que, si nécessaire (et c'est le cas, var_dump/print_r, un accès direct aux propriétés du moteur PHP souhaite les référencer). ) pthreads peut manipuler le TsHashTable en dehors de l’API définie.

  • Les verrous ne sont retenus que pendant les opérations de copie. Une fois les copies réalisées, les verrous sont libérés, dans un ordre raisonnable.

Cela signifie:

  • Lorsqu'une écriture se produit, non seulement un verrou en lecture et en écriture est maintenu, mais un verrou d'accès supplémentaire. La table elle-même est verrouillée, il est impossible pour un autre contexte de la verrouiller, de la lire, de l'écrire ou de l'affecter.

  • Lorsqu'une lecture se produit, non seulement le verrou de lecture est maintenu, mais le verrou d'accès supplémentaire est également verrouillé.

Deux contextes ne peuvent accéder physiquement ni simultanément aux mêmes données à partir de la librairie, mais les écritures effectuées dans un contexte avec une référence affecteront les données lues dans un contexte avec une référence.

Ceci n'est rien d'architecture partagée et le seul moyen d'exister est de coexister. Ceux qui sont un peu avertis verront cela, il y a beaucoup de copies en cours ici, et ils se demanderont si c'est une bonne chose. Beaucoup de copies se font dans une exécution dynamique, c'est la dynamique d'un langage dynamique. pthreads est implémenté au niveau de l'objet, car un bon contrôle peut être obtenu sur un objet, mais les méthodes - le code exécuté par le programmeur - ont un autre contexte, sans verrouillage ni copie - la portée de la méthode locale. La portée de l'objet dans le cas d'un objet pthreads doit être traitée comme un moyen de partager des données entre contextes, c'est son but. En gardant cela à l'esprit, vous pouvez adopter des techniques pour éviter de verrouiller le magasin d'objets à moins que cela ne soit nécessaire, par exemple en transmettant des variables d'étendue locales à d'autres méthodes dans un objet threadé plutôt que de les copier à partir du magasin d'objets lors de leur exécution.

La plupart des bibliothèques et des extensions disponibles pour PHP sont des enveloppes minces entourant des tiers, les fonctionnalités de base de PHP sont, dans une certaine mesure, identiques. pthreads n'est pas une mince enveloppe autour des fils Posix; c'est une API de thread basée sur Posix Threads. Il est inutile d'implémenter des Threads dans PHP que ses utilisateurs ne comprennent pas ou ne peuvent pas utiliser. Il n'y a aucune raison pour qu'une personne n'ayant aucune connaissance de ce qu'est ou n'est un mutex ne devrait pas être en mesure de tirer profit de tout ce qu'elle a, à la fois en termes de compétences et de ressources. Un objet fonctionne comme un objet, mais lorsque deux contextes se rencontrent, pthreads apporte stabilité et sécurité.

Quiconque a travaillé dans Java verra les similitudes entre un objet pthreads et le threading en Java, ces mêmes personnes auront sans aucun doute vu une erreur appelée ConcurrentModificationException - car cela ressemble à une erreur déclenchée par le Java runtime si deux threads écrivent les mêmes données physiques simultanément. Je comprends pourquoi elle existe, mais cela me laisse perplexe avec des ressources aussi peu coûteuses qu’elles sont, couplée au fait que le moteur d’exécution est capable de détecter la concurrence au moment précis et unique où la sécurité peut être atteinte pour l’utilisateur, qu’il choisit de génère une erreur potentiellement fatale au moment de l'exécution plutôt que de gérer l'exécution et l'accès aux données.

Pthreads n'émettra pas de telles erreurs stupides, l'API est écrite pour rendre le threading aussi stable et compatible que possible, je crois.

Le multi-threading n'est pas comme utiliser une nouvelle base de données, il convient de porter une attention particulière à chaque mot du manuel et aux exemples fournis avec pthreads.

Enfin, à partir du manuel PHP:

pthreads était et reste une expérience avec de très bons résultats. Chacune de ses limitations ou caractéristiques peut changer à tout moment. c'est la nature de l'expérimentation. Ses limitations - souvent imposées par la mise en œuvre - existent pour une bonne raison; pthreads a pour objectif de fournir une solution utilisable pour le multitâche dans PHP à tout niveau. Dans l'environnement exécuté par pthreads, certaines restrictions et limitations sont nécessaires pour fournir un environnement stable.

176
Joe Watkins

Voici un exemple de ce que Wilco a suggéré:

$cmd = 'Nohup Nice -n 10 /usr/bin/php -c /path/to/php.ini -f /path/to/php/file.php action=generate var1_id=23 var2_id=35 gen_id=535 > /path/to/log/file.log & echo $!';
$pid = Shell_exec($cmd);

Fondamentalement, cela exécute le script PHP sur la ligne de commande, mais renvoie immédiatement le PID, puis s’exécute en arrière-plan. (Echo $! Assure que rien d'autre n'est retourné à part le PID.) Cela permet à votre script PHP de continuer ou de se quitter si vous le souhaitez. Lorsque j'ai utilisé cela, j'ai redirigé l'utilisateur vers une autre page, où un appel AJAX est effectué toutes les 5 à 60 secondes pour vérifier si le rapport est toujours en cours d'exécution. (J'ai une table pour stocker le gen_id et l'utilisateur auquel elle est liée.) Le script de vérification exécute les opérations suivantes:

exec('ps ' . $pid , $processState);
if (count($processState) < 2) {
     // less than 2 rows in the ps, therefore report is complete
}

Il existe un court post sur cette technique ici: http://nsaunders.wordpress.com/2007/01/12/running-a-background-process-in-php/

48
Darryl Hein

En bref: oui, il y a multithreading en php mais vous devriez utiliser le multitraitement à la place.

Informations sur le backgroud: threads vs. processus

Il y a toujours un peu de confusion au sujet de la distinction des threads et des processus, je vais donc décrire brièvement les deux:

  • Un thread est une séquence de commandes que la CPU va traiter. La seule donnée qui le compose est un compteur de programme. Chaque cœur de processeur ne traite qu'un seul thread à la fois, mais peut basculer entre l'exécution de tâches différentes via la planification.
  • Un processus est un ensemble de ressources partagées. Cela signifie qu’il comprend une partie de la mémoire, des variables, des instances d’objet, des descripteurs de fichier, des mutex, des connexions à une base de données, etc. Chaque processus contient également un ou plusieurs threads. Tous les threads du même processus partagent ses ressources, vous pouvez donc utiliser une variable dans un thread que vous avez créé dans un autre. Si ces threads font partie de deux processus différents, ils ne peuvent pas accéder directement aux ressources de chacun. Dans ce cas, vous avez besoin de communication inter-processus par exemple. tuyaux, fichiers, sockets ...

Multitraitement

Vous pouvez réaliser l'informatique parallèle en créant de nouveaux processus (qui contiennent également un nouveau thread) avec php. Si vos threads n'ont pas besoin de beaucoup de communication ou de synchronisation, c'est votre choix, car les processus sont isolés et ne peuvent pas interférer avec le travail de chacun. Même si l'un se bloque, cela ne concerne pas les autres. Si vous avez besoin de beaucoup de communication, lisez "Multithreading" ou, malheureusement, songez à utiliser un autre langage de programmation, car la communication et la synchronisation entre processus donnent beaucoup à la peau.

En php, vous avez deux façons de créer un nouveau processus:

laissez le système d'exploitation le faire pour vous : vous pouvez demander à votre système d'exploitation de créer un nouveau processus et d'y exécuter un nouveau script php (ou le même) .

  • pour linux , vous pouvez utiliser ce qui suit ou considérer réponse de Darryl Hein :

    $cmd = 'Nice php script.php 2>&1 & echo $!';
    pclose(popen($cmd, 'r'));
    
  • pour fenêtres , vous pouvez utiliser ceci:

    $cmd = 'start "processname" /MIN /belownormal cmd /c "script.php 2>&1"';
    pclose(popen($cmd, 'r'));
    

faites-le vous-même avec une fourchette : php offre également la possibilité d'utiliser le forking via la fonction pcntl_fork () . Vous pouvez trouver un bon tutoriel sur la procédure à suivre ici mais je vous recommande fortement de ne pas l'utiliser, car fork est un crime contre l'humanité et surtout contre oop.

Multithreading

Avec le multithreading, tous vos threads partagent leurs ressources afin que vous puissiez facilement communiquer entre eux et les synchroniser sans trop de travail supplémentaire. De l'autre côté, vous devez savoir ce que vous faites, car les conditions de course et les blocages sont faciles à produire mais très difficiles à déboguer.

Le php standard ne fournit pas de multithreading mais il y a une extension (expérimentale) qui fait réellement - pthreads . Sa documentation api l'a même fait php.net . Avec lui, vous pouvez faire des choses comme vous pouvez dans vrais langages de programmation :-) comme ceci:

class MyThread extends Thread {
    public function run(){
        //do something time consuming
    }
}

$t = new MyThread();
if($t->start()){
    while($t->isRunning()){
        echo ".";
        usleep(100);
    }
    $t->join();
}

Pour linux , il existe un guide d'installation ici chez stackoverflow.

Pour fenêtres , il y en a une maintenant:

  • Vous avez d’abord besoin de la version thread-safe de php.
  • Vous avez besoin des versions pré-compilées des deux pthreads et de son extension php. Ils peuvent être téléchargés ici . Assurez-vous de télécharger la version compatible avec votre version php.
  • Copiez php_pthreads.dll (à partir du fichier Zip que vous venez de télécharger) dans votre dossier d’extension php ([phpDirectory]/ext).
  • Copiez pthreadVC2.dll dans [phpDirectory] (le dossier racine - pas le dossier d’extension).
  • Éditez [phpDirectory] /php.ini et insérez la ligne suivante

    extension=php_pthreads.dll
    
  • Testez-le avec le script ci-dessus avec un peu de sommeil ou quelque chose là où se trouve le commentaire.

Et maintenant, le gros [~ # ~] mais [~ # ~] : Bien que cela fonctionne vraiment, php n'était pas à l'origine conçu pour le multithreading. Il existe une version thread-safe de php et à partir de la v5.4, elle semble être presque sans bugs, mais utiliser php dans un environnement multi-thread est toujours déconseillé dans le manuel php (mais peut-être qu'ils vient de ne pas mettre à jour leur manuel à ce sujet). Un problème beaucoup plus important pourrait être que beaucoup de communes les extensions ne sont pas thread-safe . Vous pouvez donc obtenir des threads avec cette extension php, mais les fonctions sur lesquelles vous comptez ne sont toujours pas thread-safe, vous allez donc probablement rencontrer des conditions de concurrence critique, des blocages, etc. dans du code que vous n'avez pas écrit vous-même ...

24
Francois Bourgeois

Vous pouvez utiliser pcntl_fork () pour obtenir quelque chose de similaire aux threads. Techniquement, il s'agit de processus distincts, de sorte que la communication entre les deux n'est pas aussi simple avec des threads, et je pense que cela ne fonctionnera pas si PHP est appelé par Apache.

17
davr

Si quelqu'un s'en soucie, j'ai relancé php_threading (pas la même chose que les threads, mais similaire) et je l'ai en fait au point où ça marche (un peu) bien!

page du projet

Télécharger (pour Windows PHP 5.3 VC9 TS))

Exemples

README

13
Alec Gorge

pcntl_fork() est ce que vous recherchez, mais son processus ne consiste pas à fileter. vous aurez donc le problème de l'échange de données. pour les résoudre, vous pouvez utiliser les fonctions de sémaphore phps ( http://www.php.net/manual/de/ref.sem.php ) les files d'attente de messages peuvent être un peu plus faciles au début que la mémoire partagée segments.

Quoi qu’il en soit, une stratégie que j’utilise dans un cadre Web que je développe et qui charge en parallèle des blocs d’une page Web (probablement avec des requêtes externes) qui utilisent beaucoup de ressources: je fais une file d’attente pour savoir quelles données j’attends, puis j’ai hors des emplois pour chaque processus. Une fois cela fait, ils stockent leurs données dans le cache apc sous une clé unique à laquelle le processus parent peut accéder. une fois que toutes les données sont là, elles continuent. J'utilise un simple usleep() pour attendre, car la communication entre processus n'est pas possible dans Apache (les enfants perdront la connexion avec leurs parents et deviendront des zombies ...). cela m'amène à la dernière chose: il est important de tuer tous les enfants! il y a aussi des classes qui bifurquent des processus mais conservent des données, je ne les ai pas examinées, mais zend framework en a une, et elles codent généralement de manière lente mais fiable. vous pouvez le trouver ici: http://zendframework.com/manual/1.9/en/zendx.console.process.unix.overview.html Je pense qu'ils utilisent des segments shm! Enfin et surtout, il y a une erreur sur ce site Web zend, erreur mineure dans l'exemple.

while ($process1->isRunning() && $process2->isRunning()) {
    sleep(1);
}
should of course be:
while ($process1->isRunning() || $process2->isRunning()) {
    sleep(1);
}
7
The Surrican

Juste une mise à jour, il semble que PHP les gars travaillent sur le support et sa disponible maintenant.

Voici le lien qui y mène: http://php.net/manual/en/book.pthreads.php

6
happyhardik

Il existe une extension de Threading en cours de développement basée sur PThreads qui semble très prometteuse sur https://github.com/krakjoe/pthreads

6
JasonDavis

J'ai une classe PHP) qui fonctionne parfaitement dans un environnement de production depuis plus de deux ans.

EDIT: Ceci est maintenant disponible en tant que bibliothèque composer) et dans le cadre de mon framework MVC, Hazaar MVC.

Voir: https://git.hazaarlabs.com/hazaar/hazaar-thread

5
Jamie Carl

Je sais que c'est une vieille question, mais vous pouvez regarder http://phpthreadlib.sourceforge.net/

Communication bidirectionnelle, prise en charge de Win32 et aucune extension requise.

2
Unsigned

Jamais entendu parler de appserver de techdivision?

Il est écrit en php et fonctionne comme un serveur d'applications gérant des multithreads pour les applications php à fort trafic. Est encore en version bêta mais très prometteur.

1
user2627170