Accueil > > > [PHP5] MULTI-THREADING : ACCÉLÉRATION DU TEMPS DE TRAVAIL D'UN SCRIPT.
[PHP5] MULTI-THREADING : ACCÉLÉRATION DU TEMPS DE TRAVAIL D'UN SCRIPT.
Information sur la source
Description
L'article rédigé par Martin Roest (http://www.ibuildings.com/blog/archives/1539-Boos t-performance-with-parallel-processing.html) nous montre à quel point il peut-être facile de jouer avec plusieurs processus en même temps, même en PHP. J'ai personnellement été très séduit par la simplicité, mais je trouve que l'on pouvais encore faire plus simple : une petite classe :) L'utilité d'un tel code se trouve sur des scripts de traitement, tel qu'un remaniement de base de donnée, une modifications sur plusieurs fichiers, etc. Par contre, il est important de savoir que la gestion des processus tel qu'il est utilisé ici ne fonctionne pas sous Windows, et qu'il n'est pas conseillé d'appeler ce script depuis un environnement type Apache. En gros et pour faire simple, ce type de script s'utilise en ligne de commande (php-cli) et dans un environnement Unix, type cron. Pour vous prouver l'efficacité de mes dires, voici un code bateau qui utilise 5 processus simultanés pour exécuter un script :
Source
- <?php
- /**
- * @class ProcessManager
- * Class that handle creating multiple process
- *
- * @licence GNU/GPLv3
- *
- * @author Cyril Nicodème
- *
- *
- * @note : This doesn't work on Windows machine
- * @note : It is recommended to use this class in an cli environnement,
- * forking is NOT recommended running from an Apache (or some other preforking web server) module
- *
- * @see : http://www.ibuildings.com/blog/archives/1539-Boost-performance-with-parallel-processing.html
- */
- class ProcessManager {
- /**
- * Contain the Processus Id of the current processus
- *
- * @var Integer $_iPid
- */
- private $_iPid;
-
- /**
- * Contain the priority for the current processus
- *
- * @var Integer $_iPriority
- */
- private $_iPriority = 0;
-
- /**
- * Contain a list of all the childrens
- * (in case the current processus is the father)
- *
- * @var Array $_aChildrens
- */
- private $_aChildrens = array ();
-
- /**
- * Contain the number of max allowed childrens
- *
- * @var Integer $_iMaxChildrens
- */
- private $_iMaxChildrens = 2;
-
- /**
- * Constructor
- * Test if this application can be used, set the MaxChildren value,
- * retrieve his Process ID and set the signals
- *
- * @param Integer $iMaxChildrens (optional)
- *
- * @return ProcessManager
- */
- public function __construct ($iMaxChildrens = 2) {
- if (!function_exists ('pcntl_fork'))
- throw new Exception ('Your configuration does not include pcntl functions.');
-
- if (!is_int ($iMaxChildrens) || $iMaxChildrens < 1)
- throw new Exception ('Childrens must be an Integer');
-
- $this->_iMaxChildrens = $iMaxChildrens;
- $this->_iPid = getmypid ();
-
- // Setting up the signal handlers
- $this->addSignal (SIGTERM, array ($this, 'signalHandler'));
- $this->addSignal (SIGQUIT, array ($this, 'signalHandler'));
- $this->addSignal (SIGINT, array ($this, 'signalHandler'));
- }
-
-
- public function __destruct () {
- foreach ($this->_aChildrens as $iChildrensPid)
- pcntl_waitpid ($iChildrensPid, $iStatus);
- }
-
- /**
- * Fork a Processus
- *
- * @return void
- */
- public function fork ($mFunction, $aParams = array ()) {
- if (!is_string ($mFunction) && !is_array ($mFunction))
- throw new Exception ('Function given must be a String or an Array');
-
- if (!is_array ($aParams))
- throw new Exception ('Parameters must be an Array');
-
- $iPid = pcntl_fork ();
-
- if ($iPid === -1)
- throw new Exception ('Unable to fork.');
- elseif ($iPid > 0) {
- // We are in the parent process
- $this->_aChildrens[] = $iPid;
-
- if (count ($this->_aChildrens) >= $this->_iMaxChildrens) {
- pcntl_waitpid (array_shift ($this->_aChildrens), $iStatus);
- }
- }
- elseif ($iPid === 0) { // We are in the child process
- call_user_func_array ($mFunction, $aParams);
- exit (0);
- }
- }
-
- /**
- * Add a new signal that will be called to the given function with some optionnals parameters
- *
- * @param Integer $iSignal
- * @param Mixed $mFunction
- * @param Array $aParams[optional]
- *
- * @return void
- */
- public function addSignal ($iSignal, $mFunction) {
- if (!is_int ($iSignal))
- throw new Exception ('Signal must be an Integer.');
-
- if (!is_string ($mFunction) && !is_array ($mFunction))
- throw new Exception ('Function to callback must be a String or an Array.');
-
- if (!pcntl_signal ($iSignal, $mFunction))
- throw new Exception ('Unable to set up the signal.');
- }
-
- /**
- * The default signal handler, to avoid Zombies
- *
- * @param Integer $iSignal
- *
- * @return void
- */
- public function signalHandler ($iSignal = SIGTERM) {
- switch ($iSignal) {
- case SIGTERM: // Finish
- exit (0);
- break;
- case SIGQUIT: // Quit
- case SIGINT: // Stop from the keyboard
- case SIGKILL: // Kill
- exit (1);
- break;
- }
- }
-
- /**
- * Set the number of max childrens
- *
- * @param Integer $iMaxChildren
- *
- * @return void
- */
- public function setMaxChildren ($iMaxChildren) {
- if (!is_int ($iMaxChildrens) || $iMaxChildrens < 1)
- throw new Exception ('Childrens must be an Integer');
-
- $this->_iMaxChildrens = $iMaxChildrens;
- }
-
- /**
- * Return the current number of MaxChildrens
- *
- * @return Integer
- */
- public function getMaxChildrens () {
- return self::$_iMaxChildrens;
- }
-
- /**
- * Set the priority of the current processus.
- *
- * @param Integer $iPriority
- * @param Integer $iProcessIdentifier[optional]
- *
- * @return void
- */
- public function setPriority ($iPriority, $iProcessIdentifier = PRIO_PROCESS) {
- if (!is_int ($iPriority) || $iPriority < -20 || $iPriority > 20)
- throw new Exception ('Invalid priority.');
-
- if ($iProcessIdentifier != PRIO_PROCESS
- || $iProcessIdentifier != PRIO_PGRP
- || $iProcessIdentifier != PRIO_USER)
- throw new Exception ('Invalid Process Identifier type.');
-
- if (!pcntl_setpriority ($iPriority, $this->_iPid, $iProcessIdentifier))
- throw new Exception ('Unable to set the priority.');
-
- self::$_iPriority = $iPriority;
- }
-
- /**
- * Get the priority of the current processus.
- *
- * @return Integer
- */
- public function getPriority () {
- return self::$_iPriority;
- }
-
- /**
- * Return the PID of the current process
- *
- * @return Integer
- */
- public function getMyPid () {
- return $this->_iPid;
- }
- }
- ?>
-
- <?php ///////////////////////////////////////////////////////////////////////////////////////////// ?>
-
- <?php
- /**
- * Voici un exemple de ce que cela pourrait donner :
- * Dans un terminal sous linux, appelez le, et vous devriez voir la façon dont la méthode "doBigWork" est appelée (5 fois par 5 fois jusqu'à 12) (5, 5, 2)
- */
- require_once ('ProcessManager.php');
-
- function doBigWork ($iWork) {
- echo 'Sleeping for Work N° '.$iWork."\n";
- sleep (20);
- }
-
- try {
- // We instanciate the ProcessManager with 5 childs
- $oPM = new ProcessManager (5);
- }
- catch (Exception $oE) {
- die ('Your configuration does not support "pcntl" methods.');
- }
-
- for ($i = 0; $i < 12; $i++) {
- // It could happen that the script couldn't fork a process. In that case, an Exception would be raised
- try {
- $oPM->fork ('doBigWork', array ($i));
- }
- catch (Exception $oE) {
- echo 'Using non forked way :'."\n";
- doBigWork ($i);
- }
- }
- ?>
<?php
/**
* @class ProcessManager
* Class that handle creating multiple process
*
* @licence GNU/GPLv3
*
* @author Cyril Nicodème
*
*
* @note : This doesn't work on Windows machine
* @note : It is recommended to use this class in an cli environnement,
* forking is NOT recommended running from an Apache (or some other preforking web server) module
*
* @see : http://www.ibuildings.com/blog/archives/1539-Boost-performance-with-parallel-processing.html
*/
class ProcessManager {
/**
* Contain the Processus Id of the current processus
*
* @var Integer $_iPid
*/
private $_iPid;
/**
* Contain the priority for the current processus
*
* @var Integer $_iPriority
*/
private $_iPriority = 0;
/**
* Contain a list of all the childrens
* (in case the current processus is the father)
*
* @var Array $_aChildrens
*/
private $_aChildrens = array ();
/**
* Contain the number of max allowed childrens
*
* @var Integer $_iMaxChildrens
*/
private $_iMaxChildrens = 2;
/**
* Constructor
* Test if this application can be used, set the MaxChildren value,
* retrieve his Process ID and set the signals
*
* @param Integer $iMaxChildrens (optional)
*
* @return ProcessManager
*/
public function __construct ($iMaxChildrens = 2) {
if (!function_exists ('pcntl_fork'))
throw new Exception ('Your configuration does not include pcntl functions.');
if (!is_int ($iMaxChildrens) || $iMaxChildrens < 1)
throw new Exception ('Childrens must be an Integer');
$this->_iMaxChildrens = $iMaxChildrens;
$this->_iPid = getmypid ();
// Setting up the signal handlers
$this->addSignal (SIGTERM, array ($this, 'signalHandler'));
$this->addSignal (SIGQUIT, array ($this, 'signalHandler'));
$this->addSignal (SIGINT, array ($this, 'signalHandler'));
}
public function __destruct () {
foreach ($this->_aChildrens as $iChildrensPid)
pcntl_waitpid ($iChildrensPid, $iStatus);
}
/**
* Fork a Processus
*
* @return void
*/
public function fork ($mFunction, $aParams = array ()) {
if (!is_string ($mFunction) && !is_array ($mFunction))
throw new Exception ('Function given must be a String or an Array');
if (!is_array ($aParams))
throw new Exception ('Parameters must be an Array');
$iPid = pcntl_fork ();
if ($iPid === -1)
throw new Exception ('Unable to fork.');
elseif ($iPid > 0) {
// We are in the parent process
$this->_aChildrens[] = $iPid;
if (count ($this->_aChildrens) >= $this->_iMaxChildrens) {
pcntl_waitpid (array_shift ($this->_aChildrens), $iStatus);
}
}
elseif ($iPid === 0) { // We are in the child process
call_user_func_array ($mFunction, $aParams);
exit (0);
}
}
/**
* Add a new signal that will be called to the given function with some optionnals parameters
*
* @param Integer $iSignal
* @param Mixed $mFunction
* @param Array $aParams[optional]
*
* @return void
*/
public function addSignal ($iSignal, $mFunction) {
if (!is_int ($iSignal))
throw new Exception ('Signal must be an Integer.');
if (!is_string ($mFunction) && !is_array ($mFunction))
throw new Exception ('Function to callback must be a String or an Array.');
if (!pcntl_signal ($iSignal, $mFunction))
throw new Exception ('Unable to set up the signal.');
}
/**
* The default signal handler, to avoid Zombies
*
* @param Integer $iSignal
*
* @return void
*/
public function signalHandler ($iSignal = SIGTERM) {
switch ($iSignal) {
case SIGTERM: // Finish
exit (0);
break;
case SIGQUIT: // Quit
case SIGINT: // Stop from the keyboard
case SIGKILL: // Kill
exit (1);
break;
}
}
/**
* Set the number of max childrens
*
* @param Integer $iMaxChildren
*
* @return void
*/
public function setMaxChildren ($iMaxChildren) {
if (!is_int ($iMaxChildrens) || $iMaxChildrens < 1)
throw new Exception ('Childrens must be an Integer');
$this->_iMaxChildrens = $iMaxChildrens;
}
/**
* Return the current number of MaxChildrens
*
* @return Integer
*/
public function getMaxChildrens () {
return self::$_iMaxChildrens;
}
/**
* Set the priority of the current processus.
*
* @param Integer $iPriority
* @param Integer $iProcessIdentifier[optional]
*
* @return void
*/
public function setPriority ($iPriority, $iProcessIdentifier = PRIO_PROCESS) {
if (!is_int ($iPriority) || $iPriority < -20 || $iPriority > 20)
throw new Exception ('Invalid priority.');
if ($iProcessIdentifier != PRIO_PROCESS
|| $iProcessIdentifier != PRIO_PGRP
|| $iProcessIdentifier != PRIO_USER)
throw new Exception ('Invalid Process Identifier type.');
if (!pcntl_setpriority ($iPriority, $this->_iPid, $iProcessIdentifier))
throw new Exception ('Unable to set the priority.');
self::$_iPriority = $iPriority;
}
/**
* Get the priority of the current processus.
*
* @return Integer
*/
public function getPriority () {
return self::$_iPriority;
}
/**
* Return the PID of the current process
*
* @return Integer
*/
public function getMyPid () {
return $this->_iPid;
}
}
?>
<?php ///////////////////////////////////////////////////////////////////////////////////////////// ?>
<?php
/**
* Voici un exemple de ce que cela pourrait donner :
* Dans un terminal sous linux, appelez le, et vous devriez voir la façon dont la méthode "doBigWork" est appelée (5 fois par 5 fois jusqu'à 12) (5, 5, 2)
*/
require_once ('ProcessManager.php');
function doBigWork ($iWork) {
echo 'Sleeping for Work N° '.$iWork."\n";
sleep (20);
}
try {
// We instanciate the ProcessManager with 5 childs
$oPM = new ProcessManager (5);
}
catch (Exception $oE) {
die ('Your configuration does not support "pcntl" methods.');
}
for ($i = 0; $i < 12; $i++) {
// It could happen that the script couldn't fork a process. In that case, an Exception would be raised
try {
$oPM->fork ('doBigWork', array ($i));
}
catch (Exception $oE) {
echo 'Using non forked way :'."\n";
doBigWork ($i);
}
}
?>
Conclusion
Au final, sans utiliser plusieurs processus, ce code aurait pris 12*20 = 240 secondes. Avec 5 enfants, le temps de travail est divisé par ... 5, soit 48 secondes ! Quand même !
Bien entendu, vous pouvez augmenter le nombre d'enfant, tout dépendra des ressources que consomment votre fonction de travail (histoire de ne pas tuer votre machine (je l'ai fait pendant les tests :p)).
Une dernière modification qui serait sympathique, c'est d'inclure les fonctions lambdas dans la méthode fork, au lieu de l'appel à une méthode en utilisant le call_user_func_array. Mais ma configuration actuelle de Php n'est pas encore en 5.3, donc je ne peux ni jouer avec les closures, ni avec les fonctions lambdas :p Peut-être plus tard ? :)
Sources du même auteur
Sources de la même categorie
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
De process à service, apache tombe dans les choux [ par msohet ]
bonjour les gens et voila mon probleme :j'ai du réalisé une appli en php avec connexion odbc/mysql et tout le bordel que vous connaissez.Pour ce fair
De process à service, apache tombe dans les choux [ par msohet ]
bonjour les gens et voila mon probleme :j'ai du réalisé une appli en php avec connexion odbc/mysql et tout le bordel que vous connaissez.Pour ce fair
CLI / CGI : c koi çà sert à koi et comment on l'utilise ??? [ par PhoenixCB ]
Bah oui g entendu parler de çà, et g la version CLI et la version CGI .. mais ... çà sert à quoi ?Et comment on l'utilise ?P.S. : chuis sous PHP 4.3.4
Comment récuperer le résultat d'une requete dans une variable en php? [ par neilero ]
SalutJe n'arrive pas à récuperer le résultat d'une requete dans une variable en php.je ne vois pas ce qu'il ne va pas://requete permettant de recupere
probleme d'envoi email [ par pietbrouwers ]
quelqu'un peut-il me dire ce qui ne fonctionne pas dans le script suivant : je n'arrive pas a recevoir par mail le contenu de la facture. voir l
Nouveau site, mais (suite) [ par malalam ]
Hello, j'ouvre un nouveau thread pour ce sujet car d'une part l'autre est trop loin, d'autre part je ne le trouve plus malgre mes recherches... Enco
Process distant [ par JLN ]
Bonjour, Je voudrai connaitre tous les process qui tournent sur ma machine distante et les afficher avec ce systeme, as-tu un
affichage de champs [ par chris tuckers ]
Bonjour, alors, c'est une question top urgente et je bloque dessus depuis ce matin. Dans une page, je désirais voir renseigner deux tables. Voici
un else qui en renvoit pas ce que je veux [ par oceane751 ]
bonjour à tous! et joyeuses paques!voilà mon soucissi un numéro de client n'existe pas, un message informe la personne qui utiilise l'a
session_register en PHP CLI [ par motherboy ]
Bonjour à tous,Je développe actuellement un Bot IRC, linké à un IRCd Unreal, et ayant des fonctions de Anope.Donc je cherche à savoir si je peux mémor
|
Derniers Blogs
SESSION SILVERLIGHT 5 3D : SLIDES ET DEMOSSESSION SILVERLIGHT 5 3D : SLIDES ET DEMOS par Groc
Durant les techdays, j'ai eu le plaisir d'animer une session sur Silverlight 5 et la 3D avec Simon Ferquel. Comme promis, voici nos slides et mes démos (celles avec le viper BSG) ici et là. Pour mémoire, les démos utilisent toutes le viper BSG...
Cliquez pour lire la suite de l'article par Groc [TECHDAYS 2012] SESSION WEBMATRIX 2 : LE COUTEAU SUISSE GRATUIT POUR VOS DéVELOPPEMENTS WEB - SLIDES[TECHDAYS 2012] SESSION WEBMATRIX 2 : LE COUTEAU SUISSE GRATUIT POUR VOS DéVELOPPEMENTS WEB - SLIDES par gpommier
Suite à la session que j'ai présenté sur WebMatrix 2, vous pouvez trouver les slides ici, ainsi que les démos en packages nuget : démos1 et démos2 J'en profite pour remercier chaleureusement tous ceux qui sont venus très nombreux à cette sess...
Cliquez pour lire la suite de l'article par gpommier [SHAREPOINT] LES SESSIONS TECHDAYS 2012.[SHAREPOINT] LES SESSIONS TECHDAYS 2012. par Patrick Guimonet
Voici donc pour ceux qui n'ont pas pu venir, ou ceux qui n'ont pas pu toutes les suivre la liste des sessions SharePoint aux TechDays 2012, que je mettrais à jour dès que les liens des vidéo seront disponibles. Ou ici : http...
Cliquez pour lire la suite de l'article par Patrick Guimonet TECHDAYS PARIS 2012 : SESSION PLEINIèRE JOUR 3TECHDAYS PARIS 2012 : SESSION PLEINIèRE JOUR 3 par ROMELARD Fabrice
Speaker: Bernard Ourghanlian Cette session est comme chaque jour transmise en live par BrainSonic, et j'ai donc suivi cette troisième pleinière par ce moyen sur mon iPad . Elle est dédiée comme chaque année à la mise en perspective de l'é...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice MISHRA READER : UN LECTEUR RSS TRèS ZUNE STYLE EN OPEN SOURCE !MISHRA READER : UN LECTEUR RSS TRèS ZUNE STYLE EN OPEN SOURCE ! par Vko
Hier durant une session dédiée aux Techdays 2012, j'ai eu le plaisir d'annoncer la sortie de la Béta 2 de Mishra Reader. C'est quoi ? Pour les utilisateurs, c'est une vraie expérience de lecture de flux RSS sur Windows. Rien à voir avec les produit...
Cliquez pour lire la suite de l'article par Vko
Logiciels
Academy System (17.2.1.0)ACADEMY SYSTEM (17.2.1.0)Logiciel de gestion des établissements.
- élèves/étudiants (inscription, dossier, absence...)
-... Cliquez pour télécharger Academy System Easy-Planning (1.0.0.1)EASY-PLANNING (1.0.0.1)Basé sur les mêmes principes que MyPlanning, Easy-Planning permet de créer des plannings sous la ... Cliquez pour télécharger Easy-Planning COLLECTOR PLUS (3.00B)COLLECTOR PLUS (3.00B)COLLECTOR PLUS version 3.00B est un logiciel utilisant une base de données alimentée par :
- L... Cliquez pour télécharger COLLECTOR PLUS PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V7.4)PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V7.4)PONAMEDIA TV DEVIENS HELLLOOO FLASH
LA TV SUR VOTRE ORDINATEUR.
Toute une plateforme Multi... Cliquez pour télécharger PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO LettresFaciles 2011 (8.0.0.1)LETTRESFACILES 2011 (8.0.0.1)LettresFaciles est un logiciel facilitant la création et la rédaction de lettres types.
Son inte... Cliquez pour télécharger LettresFaciles 2011
|