jueves, 29 de mayo de 2008

Problema pasando parámetros por referencia en perl

En esta ocasión voy a explicar un problema existente en el paso de parámetros por referencia en perl cuando estamos trabajando con hebras. Probado en la versión 5.8.8.

En perl el paso de parámetros por referencia se hace de la siguiente forma.

#!/usr/bin/perl

use threads;

sub funcion{
my $p=shift;
${$p}=2;
print "Vale ${$p}\n";
}

my $var=1;

funcion(\$var);

print "En la hebra principal vale $var\n";

Obtenemos el valor 2 en los dos casos, es decir, se ha producido un cambio real de la variable al pasarla por referencia. Si intentamos hacer exactamente lo mismo pero en lugar de con una función normal con una hebra resulta lo siguiente.

#!/usr/bin/perl

use threads;

sub hebra{
my $p=shift;
${$p}=2;
print "Vale ${$p}\n";
}

my $var=1;

$t=threads->new(\&hebra, \$var);

$t->join();

print "En la hebra principal vale $var\n";

En este caso pese a ser el código calcado al anterior no se produce cambio en la variable. ¿Por qué sucede esto?
Cuando creamos una hebra esperamos compartir el espacio de direcciones del proceso, sin embargo en perl con cada hebra se realiza una copia de este espacio de direcciones. Así, aunque pasemos el parámetro por referencia se modifica una copia y no el original.

En C se puede comprobar que sí se modifica la variable en una situación similar.

#include <pthread.h>
#include <stdio.h>

void *hebra(void *param){
int *p=(int *)param;
*p=2;
printf("Vale %d\n",*p);

pthread_exit(NULL);
}

int main(int argc, char *argv[]){
pthread_t threads[1];
void *status;
int var=1;

pthread_create(&threads[0], NULL, hebra, (void *) &var);
pthread_join(threads[0], &status);

printf("En la hebra principal vale %d\n", var);

pthread_exit(NULL);
}

Para compartir variables en perl entre hebras la única solución que nos queda es usar share.

#!/usr/bin/perl

use threads;
use threads::shared;

share($var);

sub hebra{
$var=2;
print "Vale $var\n";
}

$var=1;

$t=threads->new(\&hebra);

$t->join();

print "En la hebra principal vale $var\n";

En esta ocasión la variable funciona como una variable global y por tanto no hay que pasarla como parámetro.

0 comentarios: