multithreading en php?

Eléphanteau du PHP | 22 Messages

15 nov. 2009, 12:30

Bonjour à tous,

Je cherche une solution pour "émuler" du multithreading en php. J'ai trouvé quelques techniques, mais elles n'ont pas l'air d'être une solution pour mon problème :(

Voilà, en 2 mots, je suis en train de créer un site de chat, et je voudrais faire un appel ajax pour aller chercher les nouveaux messages. Le javascript/ajax dans ce cas précis n'a aucun lien avec mon problème, mon problème ce passe du coté php où mon script qui va chercher les nouveaux messages utilise un:
while ( $nouveaux_messages == NULL )
{
    $nouveaux_messages = va_chercher_les_nouveaux_messages();
    sleep( 1 ); //optionel
}
Et donc le problème est que php n'ayant aucun support pour le multithreading, tant que le script ne trouve pas de nouveau message, le site entier est bloqué.

Donc voilà, y at-til une solution à ce problème que je n'ai pas encore trouvé? Ou suis-je dans l'obligation de passer à une autre technologie comme Java ou .NET?

Merci d'avance! :)

ViPHP
ViPHP | 1136 Messages

15 nov. 2009, 13:17

Salut ,

le mltithreading est dans les tuyaux mais pas encore là ..

Une autre façon de faire , c'est de remplacer chaque thread par des process , c'est donc le systéme qui gére cette exécution en parallèle .

regarde du coté des fonctions pcntl

Good luck ,
Ch.

Eléphanteau du PHP | 22 Messages

15 nov. 2009, 13:51

Cool merci, je vais regarder ça plus en détail.

Je viens de faire tourner un petit test vite fait, j'ai copié le code de leur exemple et je l'ai balancé sur mon serveur, et effectivement alors que la page entre dans un loop infini le reste de site fonctionne toujours :)

Juste une petite chose avant d'attaquer plus serieusement, dans la 1ère ligne de l'introduction je peux lire "Cette extension ne doit pas être activée pour une utilisation en serveur web, car les résultats pourraient être inattendus". J'ai donc checké le phpinfo() de mon serveur et là je peux y voir '--enable-pcntl', suis-je donc en train de me diriger vers un chemin sombre?

Merci!

PS: merci de ne pas avoir poser la question "pourquoi je n'utilise pas tout simplement un timer javascript pour aller chercher les nouveaux messages toutes les 2 sec par exemple?" ;)

Eléphanteau du PHP | 22 Messages

15 nov. 2009, 15:09

C'est encore moi :)

Bon alors, par où commencer...

Tout d'abord merci stopher, tu m'as envoyé dans la bonne direction je pense. J'ai un script qui a l'air de fonctionner puis que le reste du site n'est plus bloqué. Cependant, j'ai besoin d'un peu plus de limières car mes connaissances en php sont assez limités, et mes connaissances en *nix le sont encore plus... Du coup, je ne comprends absolument pas ce que je fais :(

Voici mon script de base qui va chercher les messages:
header('Content-Type: text/html; charset=ISO-8859-15');
require_once '../../../../inc/php/AutoLoad.php';
$wId = $_POST[ 'w' ];
$tmp = explode( '_', $wId );
$senderId = $tmp[ 1 ];
$receiverId = $tmp[ 2 ];
$messages = NULL;

while ( $messages == NULL )
{
	$messages = $context->MessageManager->GetUnreadMessages( $senderId, $receiverId );
}

$sender = $context->UserManager->Get( $senderId );
$receiver = $context->UserManager->Get( $receiverId );

echo '( [ ';

for ( $i = 0; $i < count( $messages ); $i++ )
{
	echo '{ "Id": "' . $messages[ $i ]->Id . '", "SenderUserName": "' . $sender->UserName . '", "SentAtGmt": "' . $messages[ $i ]->SentAtGmt . '", "Message": "' . $messages[ $i ]->Message . '" }';
	
	if ( $i != count( $messages ) - 1 )
		echo ', ';
}

echo ' ] )';
Ce script me retourne 1 ou plusieurs messages sous la forme d'objets JSON, mais comme je l'ai dit avant, ce script bloque le site entier.

J'ai donc copié (en partie) l'exemple trouvé sur cette page, ce qui me donne:
header('Content-Type: text/html; charset=ISO-8859-15');
require_once '../../../../inc/php/AutoLoad.php';
declare ( ticks = 1 );

$pid = pcntl_fork();
if ( $pid == -1 )
{
     die( "impossible de forker" );
}
else if ( $pid )
{
     exit(); // nous sommes le processus père
}
else
{
     // nous sommes le processus fils
}

// détachons le processus du terminal
if ( posix_setsid() == -1 )
{
    die("impossible de se détacher du terminal");
}

$wId = $_REQUEST[ 'w' ];
$tmp = explode( '_', $wId );
$senderId = $tmp[ 1 ];
$receiverId = $tmp[ 2 ];
$messages = NULL;

while ( $messages == NULL )
{
	$messages = $context->MessageManager->GetUnreadMessages( $senderId, $receiverId );
}

$sender = $context->UserManager->Get( $senderId );
$receiver = $context->UserManager->Get( $receiverId );

echo '( [ ';

for ( $i = 0; $i < count( $messages ); $i++ )
{
	echo '{ "Id": "' . $messages[ $i ]->Id . '", "SenderUserName": "' . $sender->UserName . '", "SentAtGmt": "' . $messages[ $i ]->SentAtGmt . '", "Message": "' . $messages[ $i ]->Message . '" }';
	
	if ( $i != count( $messages ) - 1 )
		echo ', ';
}

echo ' ] )';
Et voilà, le script ne bloque plus le site et fonctionne presque comme je le veux.

Maintenant, 2 problèmes:
1 - Je ne sais absolument pas ce que je fais, et je pense que le code pourrait être plus élégant, mais je ne sais pas comment puisque je ne le comprends pas :( pourriez-vous svp me reformatter ça de façon plus intélligente? Je sais que je vous demande de faire le travail à ma place et j'ai un peu honte, mais vraiment ça m'enleverait une grosse épine du pied :oops:
2 - Un fois que le script trouve les messages, l'output retourné contient d'autres éléments qui n'étaient pas la avant, voici de quoi je parle:
X-Powered-By: PHP/5.2.10 Content-Type: text/html; charset=ISO-8859-15 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache ( [ { "Id": "8b8208b7-e88b-5c73-6b32-229bd621c882", "SenderUserName": "Seb", "SentAtGmt": "2009-11-15 13:55:55", "Message": "test" } ] )
La partie en gras étant la partie que je voudrais supprimer.

Voilà, un grand merci d'avance!

ViPHP
ViPHP | 1136 Messages

15 nov. 2009, 19:10

PS: merci de ne pas avoir poser la question "pourquoi je n'utilise pas tout simplement un timer javascript pour aller chercher les nouveaux messages toutes les 2 sec par exemple?"
On parle bien de threads , qui pour moi est fortement lié à la prog système , donc js on oubli :-)

Pour ton affichage supplémentaire , j'avoue qu'un dimanche soir 30 minutes avant l'apéro .. humm ça ne me dit rien ;-)

Par contre , si j'ai bien compris , tu lances ton script ( père )

il se fork ( clone ) , puis se tue , par contre le fils prend le relais à attendre un message .. dés qu'il en réceptionne un , il l'affiche puis se termine , c'est ça ?

Eléphanteau du PHP | 22 Messages

15 nov. 2009, 19:38

Hehe, pas de problème, j'ai bien besoin d'un apéro aussi.

Ce que je veux dire c'est que je ne comprends pas bien ce pcntl, donc j'ai réussi à faire un script qui fonctionne, mais est-ce la bonne manière de le faire? Le processus que tu décris semble effectivement être quelque chose que je voudrais voir s'exécuter, mais est-ce se que mon script fait réellement?

Je ne comprends pas pourquoi dans cet exemple ils ont un else vide... qui ne sert a rien puisque dans les 2 autres conditions le script meurt. Enfin, je me demande maintenant quelle serait une façon élégante d'avoir le même résultat que j'ai là avec le script actuel.

Dans tous les cas, même avec ce script j'ai déjà une solution à mon problème de départ qui était de ne pas bloquer le site, c'est cool merci :)

ViPHP
ViPHP | 1136 Messages

15 nov. 2009, 19:49

Le fonctionnement est finalement assez simple , voilà la raison des if else ..

-> lancement du script A

..... bla bla bla

$pid = pcntl_fork();//moi le pére tt puissant , je créé un process identique à moi même ( clone )
if ( $pid == -1 )
{
die( "impossible de forker" ); // mon clone est loupé , je le signale
}
else if ( $pid ) //maintenant il y a deux processus identiques qui tourne , heu qui suis-je
{
exit(); // le pére .. bon , je n'ai plus rien à faire , je m'en vais .. retraite bien mérité .. ou pas !
}
else
{
// salut , je suis le fis .. alors que dois-je faire ? ... et bien tt ce qu'il y a en dessous ..

// actions à faire ... là
}

Ou là ..

Code : Tout sélectionner

-------------------------->START | | Pére : création clone -------------------> FILS | | | | Je n'ai plus rien a faire je suis le fils ok .. que dois-je faire | | | | X Action A | | Action B | | Action X | | Fini .. bye | | X

Seulement tu ne reste bloqué que le temps du process Pére .. le fils tourne de façon autonome .
Attention donc au bug type boucles infinies .

Voilà ..

A +

Ch.

Eléphanteau du PHP | 22 Messages

15 nov. 2009, 20:31

Excellent! Là je comprends!Merci à toi!