2009-06-12 4 views
2

Je veux un processus "éternel" qui traverse une table MySQL et génère des processus fils. Code Pseudo:Spawn et détacher le processus PHP sans partager les ressources DB de sorte que l'enfant peut quitter?

while(true) 
    $rows = SELECT * FROM workers 
    foreach($rows as $row){ 
     DELETE $row->id 
     spawn_child($row->id) 
    } 
    sleep(5) 
} 
function spawn_child($id){ 
    $pid = pcntl_fork() 
    if($pid <0){ 
     //err 
    }elseif($pid == 0){ 
     //child 
     exec("worker_program $id"); 
     exit(); 
    }elseif($pid > 0){ 
     //parent 
    } 
} 

Le problème est que lorsque le processus de l'enfant revient de l'worker_program et sort, il ferme le mysql-poignée apparemment partagée, de sorte que le processus parent obtient un « serveur Msql alla » - Erreur.

Comment résoudre ce problème? Est-ce un défaut de conception?

Comment générer et détacher un processus en PHP, sans partager les ressources de base de données, etc, de sorte que l'enfant est libre de quitter?

(j'ai essayé: setsid et bifurquer à nouveau, appelant les travailleurs à « worker_program & » au lieu de bifurquer en php, mais cela ne semble pas fonctionner du tout (bizarre) J'utilise également le PDO?.. . gars à plus php.net disent que ce comportement n'est pas un bug Ceci est sur osx et PHP5.3 (et debian))

réfs .:

php.net/bug. "Parent process lost MySQLi connection after child process gone"

Mise à jour/solution de contournement

Donc, j'ai finalement trouvé un moyen de faire face à cela. Ce qui fonctionne est d'utiliser popen pour engendrer les processus de travail. De cette façon, il semble qu'un processus complètement "frais" est créé, sans aucun partage. Je laisse ensuite l'enfant faire le forking et se détacher lui-même. Ainsi, dans le processus maître, au lieu de pcntl_fork ou exec:

$p = popen("worker_program $arg","r"); 
sleep(1); //give it time to detach (won't work otherwise. Any other ideas?) 
pclose($p); 

Et puis dans le programme des travailleurs:

#!/usr/bin/env php 
<?php 

//fully detach from parent, as proposed by the 'gurus' 
//(Why can't this be done with only one fork?) 
if(pcntl_fork()) { 
    exit(); 
} 
posix_setsid(); 
if(pcntl_fork()) { 
    exit(); 
} 
... 
+0

avez-vous eu de la chance avec cela? compris l'accord de sommeil? –

Répondre

2

Que diriez-vous d'utiliser une connexion de MySQL persistante. Et le fermer quand vous savez que vous avez terminé.

$conn = new mysqli("p:".$dbhost, $dbuser, $dbpass, $dbname); 

$conn = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass, 
    array(PDO::ATTR_PERSISTENT => true)); 
+0

J'ai essayé à la fois persistant et non-persistant. Cela n'aide pas:/Je crois que toutes les ressources (y compris les poignées de db, persistantes ou non) sont partagées avec le processus fils quand elles sont fourchues. Lorsque le processus fils se ferme, il ferme simplement toutes les poignées, partagées ou non. – 0scar

1

Que diriez-vous si vous enlevez la poignée db explicitement lorsque vous avez terminé chez l'enfant, il ne sera pas fermée à la sortie de l'enfant? Le parent doit conserver le lien de son handle, donc le lien peut ne pas être fermé.

+0

C'est une bonne idée! Arhm, je vais vérifier sur la prochaine itération à travers ce code. Je vais avec les trucs popen pour le moment .. thx! – 0scar

0

Aucun de ceux qui ont travaillé pour moi. Je l'ai fait:

// php execl type behaviour, execute and leave ... 
// the args may be passed to $command or $args 
// [email protected] 
function execl($command, $args = array()) 
{ 
    $command = escapeshellarg($command); 
    foreach ($args as $arg) 
    $command .= ' ' . escapeshellarg($arg); 
    exec($command . ' 2>/dev/null >&- /dev/null &'); 
} 
Questions connexes