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 :

Job.php
Sélectionnez
<?php

class Job
{
    public function doSomething($arg)
    {
        sleep(2);
        // complex job
        return 'ok';
    }

    public function doOtherSomething($arg1, $arg2)
    {
        sleep(1);
        // bad job
        return 'ko';
    }
}
index.php
Sélectionnez
<?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();
console
Sélectionnez
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 :

index.php
Sélectionnez
<?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());
console
Sélectionnez
php index.php // display "0"

Il est aussi possible de stopper manuellement les processus fils :

index.php
Sélectionnez
<?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é:

index.php
Sélectionnez
<?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