Immagino che ai più le due parole del titolo suonino abbastanza male, in effetti php per sua natura non supporta i thread, il che è palesemente ovvio visto che è un linguaggio di scripting nato per fare tutt’altra cosa.
Avviene però che in certi casi, se per esempio avete fatto un algoritmo in php ma vi accorgete che avreste bisogno dei thread e non avete voglia di mettervi a riscrivere tutto, è proprio necessario un modo per implementarsi i thread.
Allora potremmo usare l’operazione asincrona più naturale che il linguaggio ci fornisce, una richiesta asincrona a una pagina web.
Iniziamo subito con il codice della libreria Thread.php:
<?
class Thread{
var $func;
var $arg;
var $thisFileName;
var $fp;
var $host;
var $port;
function Thread($host,$port=""){
$this->host = $host;
if ($port != ""){
$this->port = $port;
}else{
$this->port = 80;
}
$this->thisFileName = $_SERVER["SCRIPT_NAME"];
}
function setFunc($func,$arg=false){
$i=0;
$this->arg = "";
if ($arg){
foreach ($arg as $argument){
$this->arg .= "&a[]=".urlencode($argument);
}
}
$this->func = $func;
}
function setPort($port){
$this->port = $port;
}
function setHost($host){
$this->host = $host;
}
function start(){
$this->fp = fsockopen($this->host,$this->port);
$header = "GET ".$this->thisFileName."?threadrun=1&f=".urlencode($this->func).$this->arg." HTTP/1.1\r\n";
$header .= "Host: ".$this->host."\r\n";
$header .= "Connection: Close\r\n\r\n";
fputs($this->fp,$header);
}
function getreturn(){
$flag=false;
while (!feof($this->fp)) {
$buffer = fgets($this->fp, 4096);
if ($flag){
$output .= $buffer;
}
if (trim($buffer) == ""){
$flag = true;
}
}
return unserialize(trim($output));
}
}
if (isset($_GET['threadrun'])){
$arg_str = "";
$i=0;
if (isset($_GET['a'])){
foreach($_GET['a'] as $argument){
if ($i == 0){
$arg_str .= '"'.urldecode($argument).'"';
}else{
$arg_str .= ",".'"'.urldecode($argument).'"';
}
$i++;
}
}
eval("\$return = ".$_GET['f']."(".$arg_str.");");
echo serialize($return);
exit;
}
?>
Questa libreria è stata scritta originariamente da Alex Lau, io ho fatto alcune modifiche che saranno illustrate dopo.
Ora il codice di esempio per capire come funziona (main.php):
<?php
include_once("Thread.php");
function get($num)
{
$out = file_get_contents("http://localhost:8080/tesi/crawling/worker.php?num=".urlencode($num));
return time()." ".$out;
}
for ($p=0; $p<6; $p++) {
$thread[$p] = new Thread("localhost",8080);
$thread[$p]->setFunc("get",array($p));
$thread[$p]->start();
}
for ($p=0; $p<6; $p++) {
echo $thread[$p]->getreturn()."<br/>";
}
?>
E di worker.php:
<? sleep(5-$_GET['num']); echo "thread ".$_GET['num']; ?>
L’ovvio risultato è quello di mostrare come il primo thread creato è anche l’ultimo a ritornare ovvero ha il timestamp più alto.
Nello script di esempio vediamo come si usa la classe Thread, intanto includiamo il file, poi definiamo una funzione che richiama un’altro script passandogli un parametro, questo script contiene ovviamente il codice che vorremmo rendere asicrono.
Andiamo ora a creare il thread, ad impostare il nome della funzione e i suoi parametri, è importante notare che i parametri vanno passati dentro un array sia che siano uno solo sia che siano cento.
Dopo di che lanciamo il thread, successivamente andremo a recuperare l’output della richiesta, notare che questa richiesta è bloccante stavolta, quindi la possiamo utilizzare per monitorare i thread.
Ma come funziona la classe?
Tralasciano l’implementazione il modo è molto semplice, al momento della creazione del thread la classe si salva le informazioni sulla funzione e i suoi argomenti, quando poi si fa partire, la pagina richiama se stessa ma aprendo un socket asincrono, la pagina richiamata in modo asicrono esegue ovviamente le operazioni richieste e il suo output è riferito dall’esterno con un puntatore, in pratica i thread sono creati da apache stesso nel modo più ovvio e naturale che si possa pensare.
Vi faccio notare infine che per motivi di protocollo i valori passati come argomento della funzione sono tutti convertiti in stringhe…
Spero vi sia stato utile.
Enjoy!










figo
bella!
Oggi ho aggiunto anche la funzione isalive per verificare se il thread ha terminato, in modo non bloccante, però non ho pubblicato la cosa perché con jiimbo ci siamo ripromessi di lavorare sulla libreria per renderla più completa e magari aggiungere anche un monitor o qualcosa simile al pool di thread….