I. Prérequis▲
La version du Zend Framework 2 utilisée pour cet article est la 2.0.0beta3. L'utilisation du Zend Framework 2 nécessite au minimum la version 5.3.3 de PHP. Il est aussi nécessaire d'activer les fonctions PCNTL afin de bénéficier du système de contrôle des processus.
Zend Framework 2.0 RequirementsZend Framework 2.0 Requirements
Fonctions PCNTLFonctions PCNTL
II. PHP, thread et fork▲
Si PHP ne gère pas nativement le multithread, il offre la possibilité, sous UNIX/LINUX, de « forker » le processus courant.
II-A. Thread et fork▲
Le fork est la copie conforme du processus courant. Le nouveau processus est un fils du processus courant et possède les mêmes valeurs de variables. Cependant, contrairement au thread, le fork ne partage pas la mémoire virtuelle du processus courant, ce sont bien deux processus distincts.
Lors d'un fork, le processus créé va jouer la même suite d'instructions. La seule manière de différencier le processus courant de son processus fils est leur identifiant de processus. À la différence du fork, il est possible de faire jouer au nouveau thread des instructions différentes au nouveau processus.
Pour plus d'informations sur la différence entre le thread et le fork, je vous invite à consulter cet excellent article dont je me suis largement inspiré afin d'expliquer au mieux la différence entre ces deux notions : Dinoblog : thread et forkDinoblog : thread et fork
II-B. Php et le fork▲
Les fonctions PCNTL permettent de gérer le fork de processus, ainsi que le traitement des signaux des différents processus. Cependant, la gestion des forks utilise des fonctions de bas niveau, et comme nous l'avons indiqué plus haut, il n'est pas possible de faire jouer nativement des instructions différentes à chacun des processus. C'est pour cette raison et afin de créer une couche de haut niveau que j'ai implémenté un gestionnaire de fork permettant de gérer facilement les processus de son application.
III. Le ForkManager▲
Le gestionnaire de fork permet de piloter tous les processus que l'on va créer, voici comment ajouter deux processus en leur affectant chacun une tâche précise :
<?php
class
Job
{
public
function
doSomething($arg
)
{
sleep(2
);
// complex job
return
'ok'
;
}
public
function
doOtherSomething($arg1
,
$arg2
)
{
sleep(1
);
// bad job
return
'ko'
;
}
}
<?php
$jobObject
=
new
Job();
$manager
=
new
\ZFPJ\System\Fork\ForkManager();
$manager
->
doTheJob(array
($jobObject
,
'doSomething'
),
'value'
);
$manager
->
doTheJobChild(1
,
array
($jobObject
,
'doOtherSomething'
),
array
('value 1'
,
'value 2'
));
$manager
->
createChildren(2
);
$manager
->
wait();
$results
=
$manager
->
getSharedResults();
echo $results
->
getChild(1
)->
getResult();
echo ", "
;
echo $results
->
getChild(2
)->
getResult();
php index.
php // display "ko, ok"
Une fois le manager instancié, la méthode « doTheJob » permet de définir une tâche par défaut qui sera exécutée pour chacun des processus créés, et la méthode « doTheJobChild » affecte une tâche à un processus en particulier, en fonction de son numéro de création (de 1 à X, où X est le nombre de processus créés).
La méthode « wait » force l'attente, par le processus parent, de la notification de fin d'exécution des processus créés. En effet, si le processus père se termine, les processus fils recevront le signal d'extinction. Ainsi, il est donc préférable d'attendre la fin des processus créés avant de continuer toute instruction.
Un objet de gestion de mémoire partagée est implémenté dans le gestionnaire afin d'établir une communication entre le processus courant et ses processus fils. Comme les processus « forkés » ne partagent pas de zone de mémoire entre eux, il est donc nécessaire d'allouer une zone de mémoire partagée afin qu'il puisse interagir entre eux. Par défaut, la mémoire allouée à chacun des processus est de huit octets, ce qui permet de pouvoir stocker une chaîne de huit caractères, suffisante pour de simples retours d'états d'exécutions. D'autres zones de mémoire de tailles plus importantes seront mises en place dans les prochaines versions du gestionnaire de fork, à l'aide de nouveau composant de stockage.
D'autres fonctionnalités du ForkManager sont disponibles, comme l'ajout d'un timeout sur les tâches des processus fils :
<?php
$jobObject
=
new
Job();
$manager
=
new
\ZFPJ\System\Fork\ForkManager();
$manager
->
doTheJob(array
($jobObject
,
'doSomething'
),
'value'
);
$manager
->
doTheJobChild(1
,
array
($jobObject
,
'doOtherSomething'
),
array
('value 1'
,
'value 2'
));
$manager
->
timeout(60
);
$manager
->
createChildren(2
);
$manager
->
wait();
echo intval($manager
->
isStopped());
php index.
php // display "0"
Il est aussi possible de stopper manuellement les processus fils :
<?php
$jobObject
=
new
Job();
$manager
=
new
\ZFPJ\System\Fork\ForkManager();
$manager
->
doTheJob(array
($jobObject
,
'doSomething'
),
'value'
);
$manager
->
doTheJobChild(1
,
array
($jobObject
,
'doOtherSomething'
),
array
('value 1'
,
'value 2'
));
$manager
->
timeout(60
);
$manager
->
createChildren(2
);
// do multiple tasks
$manager
->
closeChildren();
Un système de gestion de boucle est aussi proposé:
<?php
$jobObject
=
new
Job();
$manager
=
new
\ZFPJ\System\Fork\ForkManager();
$manager
->
doTheJob(array
($jobObject
,
'doSomething'
),
'value'
);
$manager
->
setAutoStart(false
);
$manager
->
createChildren(2
);
for
($i
=
0
;
$i
<
3
;
$i
++
) {
$manager
->
start();
$manager
->
wait();
$manager
->
rewind();
}
Si le partage de mémoire est activée sur ce type de boucle, celle-ci est alors automatiquement réinitialisée à chaque redémarrage depuis la méthode « start ».
IV. Conclusion et ressources▲
La librairie ZFPJ en téléchargement libre depuis github à l'url github.com/blanchonvincent/ZF2-ParallelForkManager ZF2, vous permet maintenant de gérer facilement le fork de vos processus.
D'autres modules et fonctionnalités, créés pour le Zend Framework 2, sont disponibles sur mon compte github à l'adresse https://github.com/blanchonvincent, ainsi qu'un livre sur le fonctionnement détaillé du Zend Framework 2 : « Au cœur de Zend Framework 2Au coeur de Zend Framework 2 » (sortie au début du quatrième trimestre 2012).
Retrouvez également la liste des tutoriels du Zend Framework 2 sur mon compte developpez.com : blanchon-vincent.developpez.comblanchon-vincent.developpez.com