Compartiendo para dos mundos

Hablando de programación paralela.
Tutoriales en: https://www.youtube.com/user/jambrizgdl
Twitter: @dogoteacher

Buscar este blog

sábado, 19 de diciembre de 2009

Ejemplo para introducción al paralelismo

Introduccion

Algunos alumnos dificilmente visualizan como se puede convertir un programa "en serie" en "paralelo". A continuación se presenta el caso de un programa hecho usando el paradigma de programacion orientada a objetos ( por sus siglas POO ) en C++ que es convertido a la programación paralela siguiendo el mismo paradigma de programación.

El programa en POO "serial"

En las primeras clases de lógica de programación en la Universidad Autónoma de Guadalajara [1] ( por sus siglas UAG ) se utiliza el siguiente ejercicio:

Realiza un programa para ejecutarse sobre Ms Windows usando el "símbolo del sistema" - modo real o virtual del Ms Windows[2] - que muestre un asterisco rebotando en forma diagonal por la pantalla (al chocar con sus bordes ). El programa termina cuando se presiona cualquier tecla.

**********************

asterisco.h

**********************

#include

class Asterisco

{

public:

Asterisco();

int getCol();

int getRen();

void setCol( int valor );

void setRen( int valor );

void mostrar();

int getSentido();

void setSentido( int valor );

void mover();

private:

int col;

int ren;

char simbolo;

int sentido;

void borrar();

};

**********************

asterisco.cpp

**********************

#include

#include

#include

#include "asterisco.h"

Asterisco::Asterisco()

{

simbolo='*';

sentido=0;

}

int Asterisco::getCol()

{

return col;

}

int Asterisco::getSentido()

{

return sentido;

}

int Asterisco::getRen()

{

return ren;

}

void Asterisco::setCol( int valor )

{

col= valor;

}

void Asterisco::setSentido( int valor )

{

sentido=valor;

}

void Asterisco::setRen( int valor )

{

ren= valor;

}

void Asterisco::mostrar()

{

gotoxy( getCol(), getRen() );

printf("%c", simbolo, ren );

}

void Asterisco::borrar()

{

gotoxy( getCol(), getRen() );

printf(" " );

}

void Asterisco::mover()

{

borrar();

switch ( sentido )

{

case 0: //superior derecha

if ( ren>1)

{

if (col <>

{

ren-=1;

col+=1;

}

else

sentido= 1;

}

else

sentido=2;

break;

case 1: //superior izquierda

if ( ren>1 )

{

if ( col>1 )

{

ren-=1;

col-=1;

}

else

sentido= 0;

}

else

sentido= 3;

break;

case 2: //inferior derecha

if ( ren<24>

{

if ( col<>

{

ren+=1;

col+=1;

}

else

sentido= 3;

}

else

sentido= 0;

break;

case 3: //inferior izquierda

if ( ren<>

{

if ( col > 1 )

{

ren+=1;

col-=1;

}

else

sentido= 2;

}

else

sentido= 1;

break;

}

mostrar();

}

**********************

inicio.cpp

**********************

#include

#include

#include "asterisco.h"

void main()

{

Asterisco *obj= new Asterisco();

clrscr();

obj->setCol( 20 );

obj->setRen( 18 );

obj->setSentido( 0 );

obj->mostrar();

do

{

Sleep( 200 );

obj->mover();

} while ( !kbhit() );

}

El programa usará el compilador gratuito de Borland en su version 5.5[3] de la siguiente forma:

bcc32 asterisco.cpp inicio.cpp

El programa POO paralelo

Posteriormente se solicita a los alumnos que modifiquen el anterior programa para generar varios asteriscos y que cada uno vaya rebotando en la pantalla en direcciones distintas ( el programa coloca posiciones iniciales aleatorias con un sentido inicial aleatorio ). Una posible solución es usar un arreglo de objetos de la clase asterisco. Por medio de un ciclo recorrer constantemente todos los objetos y mover un asterisco a la vez para dar la impresión al usuario que se mueven todos los asteriscos al mismo tiempo. El código es el siguiente:

********************

asterisco.h

********************

#include <>

class Asterisco

{

public:

Asterisco();

int getCol();

int getRen();

void setCol( int valor );

void setRen( int valor );

void mostrar();

int getSentido();

void setSentido( int valor );

int getTermino();

void setTermino( int valor );

void mover();

private:

int col;

int ren;

char simbolo;

int sentido;

char termino;

void borrar();

};

********************

asterisco.cpp

********************

#include <>

#include <>

#include <>

#include "asterisco.h"

Asterisco::Asterisco()

{

simbolo='*';

sentido=0;

termino='a';

}

int Asterisco::getCol()

{

return col;

}

int Asterisco::getSentido()

{

return sentido;

}

int Asterisco::getRen()

{

return ren;

}

void Asterisco::setCol( int valor )

{

col= valor;

}

void Asterisco::setSentido( int valor )

{

sentido=valor;

}

void Asterisco::setRen( int valor )

{

ren= valor;

}

void Asterisco::mostrar()

{

gotoxy( getCol(), getRen() );

printf("%c", simbolo, ren );

}

void Asterisco::borrar()

{

gotoxy( getCol(), getRen() );

printf(" " );

}

void Asterisco::mover()

{

borrar();

switch ( sentido )

{

case 0: //superior derecha

if ( ren>1)

{

if (col <>

{

ren-=1;

col+=1;

}

else

sentido= 1;

}

else

sentido=2;

break;

case 1: //superior izquierda

if ( ren>1 )

{

if ( col>1 )

{

ren-=1;

col-=1;

}

else

sentido= 0;

}

else

sentido= 3;

break;

case 2: //inferior derecha

if ( ren<24>

{

if ( col<>

{

ren+=1;

col+=1;

}

else

sentido= 3;

}

else

sentido= 0;

break;

case 3: //inferior izquierda

if ( ren<>

{

if ( col > 1 )

{

ren+=1;

col-=1;

}

else

sentido= 2;

}

else

sentido= 1;

break;

}

mostrar();

}

int Asterisco::getTermino()

{

return termino;

}

void Asterisco::setTermino( int valor )

{

termino= valor;

}

********************

inicio.cpp

********************

#include

#include

#include

#include

#include "asterisco.h"

const int CANTIDAD_ASTERISCOS= 3;

void main()

{

Asterisco *obj[ CANTIDAD_ASTERISCOS ];

int i;

clrscr();

randomize();

for(i=0; i

{

obj[i]= new Asterisco();

obj[i]->setCol( random( 80 ) );

obj[i]->setRen( random( 24 ) );

obj[i]->setSentido( random( 4 ) );

obj[i]->mostrar();

}

i=0;

do

{

Sleep( 250);

obj[i++]->mover();

i= ( i>= CANTIDAD_ASTERISCOS ? 0 : i );

} while ( !kbhit() );

}

El único cambio ocurrio en el código que contenia al main. En este momento es donde más fácilmente se puede introducir la programación paralela y la ventaja de los hilos; al controlar cada uno un asterisco. El código que se genera solo se modifica en pocos lugares.

********************

asterisco.h

********************

#include

class Asterisco

{

public:

Asterisco();

int getCol();

int getRen();

void setCol( int valor );

void setRen( int valor );

void mostrar();

int getSentido();

void setSentido( int valor );

void iniciar(); //nuevo método

int getTermino();

void setTermino( int valor );

private:

int col;

int ren;

char simbolo;

int sentido;

char termino;

void borrar();

void mover(); //ahora es privado

};

********************

asterisco.cpp

********************

#include

#include

#include

#include "asterisco.h"

Asterisco::Asterisco()

{

simbolo='*';

sentido=0;

termino='p';

}

int Asterisco::getCol()

{

return col;

}

int Asterisco::getSentido()

{

return sentido;

}

int Asterisco::getRen()

{

return ren;

}

void Asterisco::setCol( int valor )

{

col= valor;

}

void Asterisco::setSentido( int valor )

{

sentido=valor;

}

void Asterisco::setRen( int valor )

{

ren= valor;

}

void Asterisco::mostrar()

{

gotoxy( getCol(), getRen() );

printf("%c", simbolo );

}

void Asterisco::borrar()

{

gotoxy( getCol(), getRen() );

printf(" " );

}

void Asterisco::iniciar() //contiene el ciclo que antes estaba en el main

{

termino='a';

do

{

Sleep( 200 );

mover();

} while ( !kbhit() );

termino='f';

}

void Asterisco::mover()

{

borrar();

switch ( sentido )

{

case 0: //superior derecha

if ( ren > 1)

{

if (col <>

{

ren-=1;

col+=1;

}

else

sentido= 1;

}

else

sentido=2;

break;

case 1: //superior izquierda

if ( ren > 1 )

{

if ( col > 1 )

{

ren-=1;

col-=1;

}

else

sentido= 0;

}

else

sentido= 3;

break;

case 2: //inferior derecha

if ( ren <>

{

if ( col <>

{

ren+=1;

col+=1;

}

else

sentido= 3;

}

else

sentido= 0;

break;

case 3: //inferior izquierda

if ( ren <>

{

if ( col > 1 )

{

ren+=1;

col-=1;

}

else

sentido= 2;

}

else

sentido= 1;

break;

}

mostrar();

}

int Asterisco::getTermino()

{

return termino;

}

void Asterisco::setTermino( int valor )

{

termino= valor;

}

********************

inicio.cpp

********************

#include

#include

#include

#include "asterisco.h"

#include

DWORD WINAPI iniciarAsterisco(LPVOID param);

const int CANTIDAD_HILOS = 3;

Asterisco *obj[ CANTIDAD_HILOS ];

void main()

{

HANDLE hilos[ CANTIDAD_HILOS ];

int i;

int *valorEntero;

void *parametroVoid= NULL;

randomize();

clrscr();

for( i=0; i <>

{

obj[ i ] = new Asterisco();

obj[ i ] -> setCol( random(80) );

obj[ i ] -> setRen( random(24) );

obj[ i ] -> setSentido( random(5) );

obj[ i ] -> mostrar();

}

parametroVoid= malloc( sizeof(int ) );

valorEntero= (int *) parametroVoid;

for( i=0; i<>

{

*valorEntero= i;

if ( ( hilos[i]= CreateThread( NULL, 0, iniciarAsterisco, parametroVoid, 0, NULL ))

        == NULL )

printf("error en hilo 0");

Sleep( 500 );

}

WaitForMultipleObjects( CANTIDAD_HILOS, hilos, true, INFINITE );

}

DWORD WINAPI iniciarAsterisco(LPVOID param)

{

int *i= (int *) param;

obj[ (*i) ]->iniciar();

return 0;

}

Nota: En algunas computadoras se generan más asteriscos de los esperados por errores en el manejo de la memoria.

La instrucción CreateThread se utiliza en un ciclo para generar 3 hilos, uno para cada asterisco. Hay muchas otras diapositivas y artículos donde se habla de esta instrucción por eso se no consideró oportuno hablar de ella.

Trabajos futuros

Este código evidencía el uso del tipo de dato "void" . Posteriormente en otros ejemplos se profundizará su estudio.

  • En esta ocasión se usó un arreglo de hilos que manejaban objetos de la misma clase, pero cuando estos sean diferentes ¿qué cambios habrá?
  • Realizar este juego usando una interface gráfica de windows.
  • En el ejemplo se utiliza la función Sleep para detener el flujo principal ( el main del programa ) entre cada generación de hilo, esto es por la variable compartida que se pasa como parámetro para inicializar cada hilo. De no hacerlo ¿qué sucede?

Este es un ejercicio que hará reflexionar a los estudiantes sobre la forma de trabajar de los hilos y que es tratada en varios de los cursos en línea ubicados en el sitio de Intel [4]

Referencias

[1] Universidad Autónoma de Guadalajara. http://www.uag.mx

[2] Real Mode of Ms Windows. Wikipedia. http://en.wikipedia.org/wiki/Real_mode

[3] Borland C++ Compiler version 5.5 Free Download. Embarcadero developer network. http://edn.embarcadero.com/article/20633

[4] Intel Software Network, Parallel Programming and Multi-Core. Multi – core Programming for Academia ( spanish version ). Programango Hilos en Windows. http://software.intel.com/en-us/articles/multi-core-programing-for-academia-spanish-version-1/28/oct/2008


No hay comentarios:

Publicar un comentario

Are you ready?