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
L'INTERFACE NATURELLE DE WINDOWS PHONE 7 SERIESL'INTERFACE NATURELLE DE WINDOWS PHONE 7 SERIES par odewit
La tendance est aux interfaces naturelles (NUI), et le keynote de Bill Buxton au MIX l'a bien souligné.
La charte graphique et ergonomique de Windows Phone 7 a donc été entièrement repensée en vue d'obtenir un maximum d'efficacité sur ce point. En re...
Cliquez pour lire la suite de l'article par odewit COMMENT MAPPER UNE VUE SQL SUR UNE COLLECTION DE COMPLEX TYPE?COMMENT MAPPER UNE VUE SQL SUR UNE COLLECTION DE COMPLEX TYPE? par Matthieu MEZIL
Avec EF, les vues doivent être mappées sur des entity types. Le problème c'est que les entity types doivent avoir une clé. Avec EF, nous avons les complex type qui n'ont pas de clé mais les vues ne peuvent pas être mappées dessus. Avec EF4, il est possibl...
Cliquez pour lire la suite de l'article par Matthieu MEZIL [WF4] UN BINDING ACTIVITY/ACTIVITYDESIGNER QUI PASSE MAL?[WF4] UN BINDING ACTIVITY/ACTIVITYDESIGNER QUI PASSE MAL? par JeremyJeanson
Certain d'entre vous on peut être vécu cette situation embarrassante après quelques temps passer avec WF4 : Au début avec mon " ActivityDesigner" , tout allait bien. Et puis un jour j'ai au des problèmes de " Binding" . Alors nous sommes allé sur le site ...
Cliquez pour lire la suite de l'article par JeremyJeanson
Forum
MYSQL PROBLEMEMYSQL PROBLEME par remitete
Cliquez pour lire la suite par remitete
Logiciels
Academy System (10.9.4.0)ACADEMY SYSTEM (10.9.4.0)Logiciel de gestion des établissements.
- élèves/étudiants (inscription, dossier, absence...)
-... Cliquez pour télécharger Academy System Xilisoft Convertisseur Vidéo Ultimate (5.1.39.0305)XILISOFT CONVERTISSEUR VIDéO ULTIMATE (5.1.39.0305)Xilisoft Convertisseur Vidéo Ultimate est un outil puissant de conversion vidéo, facile à utilise... Cliquez pour télécharger Xilisoft Convertisseur Vidéo Ultimate Xilisoft DVD Ripper Ultimate (5.0.64.0304)XILISOFT DVD RIPPER ULTIMATE (5.0.64.0304)Xilisoft DVD Ripper Ultimate est un logiciel excellent pour copier et convertir DVD vers presque ... Cliquez pour télécharger Xilisoft DVD Ripper Ultimate Rigs of Rods (63.3)RIGS OF RODS (63.3)c'est un jeu de multi-simulation camions,autobus voitures, avions, bateaux, hélicoptère avec défo... Cliquez pour télécharger Rigs of Rods
Comparez les prix

HTC Hero
Entre 550€ et 550€
|