Download ESTUDIO COMPARATIVO DEL SOPORTE MULTIHILO DE JAVA

Document related concepts
no text concepts found
Transcript
ESTUDIO COMPARATIVO DEL SOPORTE MULTIHILO DE JAVA VERSUS
PTHREADS
JORGE A. CASTELLANOS D.
Departamento de Computación
Universidad de Carabobo
FACYT
Valencia, Estado Carabobo, Venezuela
email: [email protected]
RESUMEN
En este trabajo, se propone el estudio de dos de
las interfaces de programación paralela de mayor
uso y difusión, como son, Pthreads de “C” y Java
(multi-threading), con el propósito principal de
hacer más accesible al estudiante de la Licenciatura en Computación de la FACYT, el tema de la
programación multihilos, que ha sido tratado tradicionalmente cómo difı́cil, proclive a errores y
algo misterioso. Para la realización del presente
estudio se seleccionó el sistema operativo Linux
debido a su Licencia GNU (de dominio público),
este hecho lo hace muy accesible a la comunidad
docente y estudiantil de la FACYT. Este sistema,
además tiene entre sus haberes: su compatibilidad con sistemas operativos tipo Unix y dispone
de una gran cantidad de herramientas de programación y aplicaciones.
PALABRAS CLAVE
Programación multihilo, Pthreads, Java, Mutex,
Monitor
1.
Introducción
Aunque se debe reconocer que la programación multihilos, de por sı́ es compleja, debido
a los factores adicionales que debe tomar en
cuenta el programador y también a los nuevos
problemas que aparecen (ı́nter-bloqueos y condiciones de carrera)[1], es importante disponer
de una evaluación inicial de las facilidades e
inconvenientes que presentan los soportes de
programación multihilos de más amplia difusión
en la actualidad como los son Pthreads y Java.
Esta evaluación le permitirá al programador,
antes de iniciar el desarrollo de una aplicación
multihilos, decidir cual de estos soportes favorece más a sus intereses, ó por otra parte decidir
el buscar otra alternativa diferente de “C” y Java
para dar solución a su problema.
Si bien, todos los códigos que se recopilaron
y desarrollaron para el presente trabajo, se
probaron usando Linux sobre máquinas Pentium
4 con un solo procesador, se podrán montar
y evaluar con relativa facilidad usando otros
sistemas operativos o tipos de máquina (por
ejemplo, multiprocesador), aunque la evaluación
de su comportamiento puede ser el objeto de un
proyecto futuro.
En la primera parte se presenta la comparación
de los dos soportes multihilos estudiados que toma en consideración las caracterı́sticas comunes
y no comunes de los dos soportes multihilo bajo
consideración. En la segunda parte del estudio
se presenta un ejemplo sencillo que además de
ilustrar el uso de las primitivas de sincronización,
sirve de referencia al lector para interpretar los
tópicos abordados en las comparaciones y en las
conclusiones.
Es de notar que este artı́culo representa un
resumen de un trabajo extenso elaborado por
el mismo autor[3], donde se implementó un
conjunto de programas multihilo que permitieron
evaluar diversos aspectos de la programación
multihilo, como los tiempos de ejecución (caso
programa del agente viajero); aún cuando solamente se presentan los códigos para uno de
los programas realizados, algunas conclusiones
hacen referencia a otros programas que no se
presentaron en este artı́culo por limitaciones de
espacio.
2.
2.1.
Comparación Java vs. Pthreads
Principales diferencias
La interfaz de Java es orientada a objetos
mientras que Pthreads está orientada a funciones. Esto hace que la interfaz de Java sea
más fácil de entender. Un hilo se conceptualiza en Java como un objeto ejecutándose sobre si mismo[12].
Los hilos Pthreads ofrecen muchas más
caracterı́sticas que Java. Por ejemplo Java
ofrece sólo un método para destruir un hilo
mientras que Pthreads ofrece varias funciones para el mismo propósito.
El poder los hilos de Java es su simplicidad,
pocas alternativas, facilidad de uso y confort.
Pthreads le da al programador un control
más preciso sobre los hilos, mientras que Java le da simplicidad y extensibilidad.
Hay un sentimiento que los hilos de Pthreads
son más difı́ciles de entender y programar
con ellos que los hilos de Java.
2.2.
Diferencias Especı́ficas
Creación y arranque de hilos: En Java un
hilo se crea instanciando un objeto thread.
Los hilos Pthreads se crean llamando la función pthread create con las funciones
especı́ficas que van a ser ejecutadas por el
hilo.
Los parámetros del nuevo hilo Pthreads
están limitados a uno, salvo que se use como
parámetro una estructura. Mientras que en
Java virtualmente no hay un lı́mite para el
número de parámetros que tendrá el método
run().
Cuando se crea un hilo con Pthreads, el nuevo hilo arranca automáticamente, mientras
que en Java se debe llamar explı́citamente
al método start(). Es decir, los hilos de
Java se pueden crear para un uso posterior,
mientras que los hilos Pthreads deben crearse donde se necesitan para empezar su ejecución.
Uniendo hilos: La unión de hilos es similar
en Java y Pthreads. Ambos crean una dependencia entre los dos hilos, donde un hilo espera a que el otro finalice su trabajo. Cuando un hilo termina su trabajo en Pthreads,
su resultado está automáticamente disponible como estado de realización (completion
status). En Java para obtener el resultado del
trabajo de un hilo que termina hay que usar
una variable de clase, y almacenar el resultado en una variable de clase. En resumen, esto resulta algo más complicado en Java que
en Pthreads.
Deteniendo un hilo: Java tiene solo un
método implementado(stop() para detener un hilo (sólo en la versión JDK 1.0). Este
método puede ser llamado desde dentro de
este hilo o desde otros objetos. Para llamar
éste método desde otros objetos, es necesario tener una referencia de los objetos hilo
para llamar stop(). En la versiones posteriores a la 1.1 (Java 2), no está implementado el método stop() porque puede dar
lugar a fallos graves del sistema[12].
En contraste, el estándar Pthreads define diferentes funciones para detener un hilo. La
función pthread exit() puede llamarse desde el hilo mismo que se quiere detener. Las funciones pthread kill() y
pthread cancel() se usan para terminar un hilo desde otros hilos[2].
Identificador del hilo: Los identificadores
de hilos (ID) están definidos en forma muy
diferente en Java y en Pthreads. Un identificador de hilo en Java es una cadena (String),
mientras que en Pthreads es semejante a una
estructura de datos opaca sobre la cual sólo
puede operar un hilo de control. En Pthreads, al contrario de Java, no es posible establecer un identificador, solo se puede recuperar y comparar. En Pthreads los identificadores de hilos se establecen automáticamente en el momento de crear el nuevo hilo. Es más fácil comparar dos hilos en Java; en Pthreads se necesita usar una función (pthread equal(id1, id2)) especialmente definida para este fin.
Atributos de los hilos: Los atributos de
los hilos son una de las mayores ventajas
de los hilos Pthreads sobre los hilos de
Java. Los atributos no están disponibles
en Java. Los atributos se usan para varios propósitos. Por ejemplo la función
pthreads attr setschedpolicy()
puede establecer polı́ticas de planificación
de hilos. En este sentido los hilos Pthreads
son más controlables que los hilos de Java.
Los hilos Pthreads tienen un atributo de
alcance de los argumentos que se puede
definir sobre múltiples hilos, mientras que
en Java no existe este atributo.
Grupos de hilos: En Java, a diferencia de
Pthreads, cada hilo, desde el momento de su
creación pertenece a un grupo de hilos. esta
caracterı́stica puede ser ventajosa para programas donde se necesita trabajar con gran
cantidad de hilos, ya que los hilos se pueden hacer pertenecer a diferentes grupos y
de esta forma las operaciones sobre los hilos
se realizan a través del grupo al cual pertenecen. En Java también es posible la definición
de grupos de grupos de hilos[11].
Niveles de Prioridad: Los hilos Pthreads
pueden tener al menos 32 prioridades diferentes, dependiendo de la implementación
utilizada. En Java solo hay 10 prioridades de
biblioteca definidas.
Polı́tica de Planificación La polı́tica de planificación determina como interactúan los
hilos de igual prioridad. Pthreads permite
definir la polı́tica de planificación, dependiendo del sistema operativo y de la implementación particular de Pthreads sobre el
sistema operativo en particular.
En Java no se tiene acceso a la polı́tica de
planificación, la interacción entre múltiples
hilos en Java depende sobre qué sistema
operativo está corriendo la máquina virtual
de Java[8].
3.
Ejemplo de programación
Este ejemplo de programación multihilo
que denominaremos “Suma Paralela” es muy
sencillo y su carácter es netamente académico.
El objeto principal es ilustrar las técnicas fundamentales de programación multihilos. El código
del programa se tomó de [4] y se adaptó para este trabajo, aun cuando su versión original es para
Modula-3.
El programa recibe como parámetro el
número de componentes de un vector (N), cuyas
componentes (v[i]) se generarán en forma aleatoria. El programa por su parte calculará en paralelo la suma de todas las componentes del vector
(v[i]), por supuesto usando múltiples hilos. Es
decir, el programa principal después de generar
el vector, creará tantos hilos como componentes
definidas del vector. Cada uno de los hilos recibe
como parámetro una componente del vector que
sumará a un acumulador común s. Cuando todos los hilos hayan terminado su trabajo (sumar
al acumulador el parámetro recibido), el programa principal imprime el resultado.
Para implementar cada una de las versiones se tomaron en consideración las facilidades
que provee cada interfaz, es decir, cuando se hace
la implementación con Pthreads se usa el objeto
mutex[13] mientras que para la implementación
con Java se prefiere usar monitores[6] para resolver los problemas de sincronización[14].
En la figura 1 se observa una salida tı́pica
obtenida al ejecutar el programa de suma paralela. Las versiones implementadas producen una
salida por pantalla equivalente a la mostrada.
jcasteld@cu-d01:˜/Ascenso/Pthreads\$ ./run
Se sumarán 10 números:
85 40 79 80 92 20 34 77 28 56
Hilo 1: sumando 85, suma 85
Hilo 2: sumando 40, suma 125
Hilo 3: sumando 79, suma 204
Hilo 4: sumando 80, suma 284
Hilo 5: sumando 92, suma 376
Hilo 6: sumando 20, suma 396
Hilo 7: sumando 34, suma 430
Hilo 8: sumando 77, suma 507
Hilo 9: sumando 28, suma 535
Hilo 10: sumando 56, suma 591
El total es 591, calls = 10
Figura 1. Salida para el programa de suma.
3.1.
Implementación con Pthreads
Para implementar este programa usando
Pthreads se realizó una versión que ilustra el uso
del objeto mutex. Pero, porqué usar un objeto de
sincronización para un programa que resuelve un
problema tan simple como: ¿ sumar “N” componentes de un vector ?
Por supuesto que sumar “N” componentes
de un vector en forma secuencial no presenta ninguna dificultad, ni amerita el uso de objetos de
sincronización. Los inconvenientes se presentan
cuando se propone obtener una solución mediante una versión paralela (multihilos).
El primer problema se genera cuando definimos una variable s que va a ser compartida por
N hilos que se ejecutan en paralelo. Es decir, se
debe garantizar el acceso exclusivo[14] a la varible s, o sea, sólo un hilo puede acceder en forma
simultánea la variable s.
El segundo problema que ilustra este ejemplo es la necesidad de crear un mecanismo de
sincronización que permita al programa principal
imprimir el resultado cuando todos los N hilos hayan finalizado.
Es por esto que necesitamos hacer uso de
los objetos de sincronización que proporciona la
interfaz de programación paralela. Para esta situación particular Pthreads nos ofrece el objeto
mutex.
En sentido estricto no se tendrán N hilos,
sino N instancias de un mismo hilo. El código
de este hilo se muestra en la figura 2. La variable global m del tipo pthread mutex t, se
usa para garantizar el acceso exclusivo al acumulador s, que es una variable compartida por
los N hilos. Cuando un hilo obtiene el mutex
m mediante la función pthread mutex lock,
este hilo puede modificar el acumulador s y
al mismo tiempo también evita que cualquier
otro hilo obtenga el mutex hasta que el hilo actual (que posee el mutex), haya liberado el mutex m a través de la ejecución de la función
pthread mutex unlock.
void * sum(void * arg) {
int * p; p = (int *)arg;
pthread_mutex_lock(&m);
s = s + *p;
pthread_mutex_unlock(&m);
}
Figura 2. Hilo que efectúa la suma.
La región que se define entre la ejecución de las funciones pthread mutex lock
y pthread mutex unlock se denomina “región crı́tica”. En programación multihilos con
Pthreads se debe tener especial cuidado en aparear correctamente estas dos funciones, para evitar caer en uno de los errores más frecuentes[4].
Esto es, debe prestarse especial cuidado en ejecutar primero la función pthread mutex lock
(al inicio de la región crı́tica) y después la función pthread mutex unlock (al final de la
región crı́tica). Este error es difı́cil de detectar, de
allı́ que usualmente se agrega un sangrado adicional al código contenido en la región crı́tica como
se muestra en la figura 2.
Según se habı́a comentado, el segundo problema que debemos resolver en este ejemplo es
imprimir el resultado cuando todos los hilos hayan terminado su trabajo (sumar su parámetro
al acumulador s). El programa principal necesita que cada uno de los hilos le informe cuando
terminó su trabajo. Una posible solución es que
cada hilo (cuando termine) incremente una variable global (que se llamará calls) y además
señale al programa principal a través de una variable done (del tipo pthread cond t ) después de modificar la variable global calls. La
nueva versión que incorpora estas mejoras puede
verse en la figura 3.
void * sum(void * arg) {
int * p; p = (int *)arg;
pthread_mutex_lock(&m);
s = s + *p;
pthread_mutex_unlock(&m);
pthread_mutex_lock(&mm);
calls ++;
pthread_cond_signal(&done);
pthread_mutex_unlock(&mm);
}
Figura 3. Código mejorado del hilo sum
En el código mejorado (figura 3) se observa el uso de un nuevo mutex mm que se usa para
garantizar el acceso exclusivo de la variable global calls. Para proteger la variable calls se
usa otro mutex para evitar caer en el problema
de “serialización” que incrementa los tiempos de
ejecución al crear cuellos de botella adicionales
en detrimento de la concurrencia (o paralelismo).
En este caso se debe notar que no es necesario
bloquear el acceso a la variable calls, mientras
se está accediendo la variable s. De esta forma
se permite a un hilo modificar la variable calls,
mientras que otro hilo puede estar modificando la
variable s. Cuando se necesita mejorar el rendimiento, se recomienda asignar a cada elemento o
dato compartido un mutex diferente.
Si bien, se conoce el código que ejecuta cada uno de los hilos, se revisará a continuación el
código del programa principal main (ver figura 4) que recibe la información de los hilos para
comprender el mecanismo de sincronización que
le permite al programa main determinar cuando
todos los hilos han terminado para luego imprimir el resultado de la suma de las componentes
del vector v.
main (int argc, char *argv[]){
/* Inicializa vector de N componentes */
/* Crea N hilos */
pthread_mutex_lock(&mm);
while (calls != N)
pthread_cond_wait(&done, &mm);
pthread_mutex_unlock(&mm);
printf("El total es %d\n", s);
}
Figura 4. Código del programa principal.
Antes de proceder a llamar a la función
pthread mutex lock, el programa main genera el vector v con N componentes y crea los N
hilos (que efectúan las sumas). Técnicamente este segmento de programa puede reemplazarse por
una serie de llamadas a la función join, pero se
prefiere usar este mecanismo porque además de
ser más eficiente, ya que no consume tiempo de
procesador por espera ocupada (como es el caso
de la función join), permite ilustrar el mecanismo estándar que se recomienda utilizar cada vez
que un hilo debe esperar por una variable de condición.
Cuando se trabaja con variables de condición (ver figura 3), el primer paso consiste en adquirir el mutex (mm) asociado a la variable compartida calls dentro del código del hilo. Este
mutex (mm) se asocia tanto a la variable calls
como a la condición done. El segundo paso es
llamar a pthread cond wait dentro de un lazo while (ver figura 4), esto es porque el valor
que devuelve pthread cond wait no indica
con certeza el valor de su predicado, es decir, se
debe re-evaluar la condición una vez que se retorna de ella; en nuestro caso debemos re-evaluar el
valor de N para estar seguros que el hilo se reasumió porque N cambió su valor. El lazo while y
el manejo de cualquier variable asociada a la evaluación de la condición (en nuestro caso N) deben
estar ubicados dentro de la región crı́tica definida por el mismo mutex (mm) que además es un
parámetro de la función pthread cond wait.
Al evaluar como falsa la condición del while es
porque todos los hilos han terminado y según lo
previsto es el momento de imprimir el resultado
y terminar la ejecución del programa main.
3.2.
Suma paralela usando Java
A diferencia de Pthreads, el lenguaje de programación Java ofrece un mecanismo de alto nivel para el manejo del problema de exclusión
mutua denominado “monitor”, que presenta la
ventaja de ser menos proclive a errores; además,
como el monitor está implementado en el lenguaje de programación Java, facilita al programador
la tarea de definir regiones crı́ticas para exclusión
mutua de datos y/o funciones.
Aún cuando el problema que se pretende resolver usando monitores es relativamente simple,
tenemos que el código en Java se torna algo complejo; posiblemente se deba al hecho que Java
es una lenguaje que “aparentemente” no está formulado para la solución de problemas pequeños.
A pesar que la solución es algo más complicada
por la cantidad de lineas de código, a diferencia
del lenguaje “C”, el compilador de Java garantiza que el programa no se ejecutará hasta tanto no
estén resueltos “casi” todos los problemas que involucren inconsistencias en las definición de las
clases que conforman el programa.
Si bien, el problema posiblemente se pueda
plantear usando menos clases, la solución se presenta usando cuatro clases, sacrificando cantidad
de código por claridad en el planteamiento y tratando de asemejar, en lo posible, la solución con
monitores a la solución ya planteada con mutex
(Pthreads), para facilitar al lector el seguimiento en la forma como se presenta la solución del
problema.
La primera clase propuesta se denomina
Acumulador (ver figura 5) y dentro de ella se
definen tres métodos del tipo synchronized,
es decir que se procesan en forma exclusiva por
la máquina virtual de Java. Por ejemplo, cuando
se ejecuta el primero de ellos, los otros dos restantes no se ejecutan hasta que el primero termina su ejecución. El primer método, sumar, suma
al acumulador s el número entero que se le pasa
como parámetro. El segundo método, icalls,
incrementa en 1 el valor de la variable calls. El
tercer método, verificar, comprueba si el valor que se le pasa como parámetro es igual a la
variable de clase calls; si son iguales regresa
true, sino devuelve false.
La segunda clase, denominada Hilo, como
lo indica su nombre (ver figura 6), contiene el
código que ejecutará cada uno de los hilos responsables de sumarle al acumulador una componente del vector. Esta clase hace uso de los
métodos definidos en la clase Acumulador
ya que se le pasa como uno de sus parámetros un objeto de clase Acumulador. Al hacer uso de los métodos tipo synchronized
de la clase Acumulador, se garantiza en for-
class Acumulador{
int s, calls;
Acumulador() { s = 0; calls = 0; }
synchronized void sumar(int num){
s = s + num;
}
synchronized void icalls(){
calls++;
}
synchronized boolean verificar(int d) {
if (d == calls) return true;
return false;
}
}
Figura 5. Clase Acumulador.
ma implı́cita la exclusión mutua en cada una de
las operaciones, es decir, sumar al acumulador
(a.sumar(numero)) e incrementar la variable
calls (a.icalls()). Nótese que justo antes
de terminar el código del hilo se usa la función
notify() (ver figura 6) para avisarle a otro hilo (en este caso Principal) que se ha realizado
la suma y se ha incrementado el valor de la variable de clase calls.
class Hilo extends Thread{
Acumulador a;
int numero;
Hilo(Acumulador acc, String id, int f){
super(id); // Nombre para la super clase
a = acc; numero = f;
}
public void run(){
try{
a.sumar(numero);
a.icalls();
notify();
}catch(Exception e){}
}
}
Figura 6. Clase Hilo.
Según ya se anticipó, la clase Principal
(ver figura 7) se torna en el hilo responsable de
imprimir el resultado de la suma, después de esperar que los otros N hilos hayan finalizado su
trabajo (sumar al acumulador un componente del
vector).
Inicialmente el hilo Principal se suspende cuando ejecuta la función wait(), después de determinar que aún el contenido de la
variable calls es diferente de N. Cuando alguno de los hilos sumadores ejecuta la función
class Principal extends Thread{
Acumulador a; int Done;
Principal(Acumulador acc, int d, String id){
super(id);
a = acc; Done = d;
}
public void run(){
while(!a.verificar(Done)){
try{
wait();
}catch(Exception e){}
}
System.out.println("El total es " + a.s);
}
}
Según se aprecia en este ejemplo, una de las
ventajas del código en Java es que es más coherente al momento de ser interpretado, a diferencia del código en lenguaje “C”, que a pesar de
ser mucho más compacto es más difı́cil de leer,
comprender y depurar. También influye el hecho
que la sintaxis de las funciones de la biblioteca
Pthreads no se presenta amigable al programador
novel que se inicia en el campo de la programación multihilo.
Figura 7. Clase Principal.
4.
notify()(ver figura 6), el hilo (Principal
p) “despierta” y re-evalúa la condición de finalización, es decir si calls es diferente de N.
Cuando la condición se evalúa como falsa, entonces se imprime el valor acumulado en la variable
de clase s.
class SumaParalela{
public static void main(String args[]){
// Inicialización de variables
Acumulador acc = new Acumulador();
// Inicializa vector de N componentes
Principal p = new Principal(acc,
N,"Hilo Principal");
p.start();
for(i=0; i<N; i++){
new Hilo(acc, " Hilo " + (i+1),
v[i]).start();
}
}
}
Figura 8. Clase SumaParalela.
La cuarta y última clase (ver figura 8) denominada SumaParalela, es la encargada de
generar (aleatoriamente) el vector de N componentes, además de crear los objetos de clase
Acumulador, Hilo y Principal, responsables de efectuar la suma paralela. Mediante un
lazo for se crean N objetos de clase Hilo; cada
uno de ellos será responsable de sumar una componente del vector al acumulador s. Nótese que
el código de la clase Principal puede estar incluido en esta clase (SumaParalela) y de esta
forma pudiera evitarse la creación de un hilo adicional.
Conclusiones
Una vez culminado el presente trabajo, se
puede concluir que las dos interfaces estudiadas
son apropiadas, siempre y cuando se utilicen para obtener su máximo provecho. La interfaz Pthreads de “C”, nos permite obtener muy buenos
tiempos de ejecución pero con un alto costo de
desarrollo; el programador debe conocer el problema con mucho detalle y debe invertir mucho
tiempo en la revisión del código multihilo, ya que
el compilador da muy poca ayuda en la búsqueda de errores. Java por su parte permite obtener
una rápida solución, pero con un alto consumo
de recursos de cómputo. Si se quiere mejorar la
primera solución, se debe gastar gran cantidad de
tiempo para su optimización. Java maneja mejor
los problemas con alta complejidad, mientras que
“C” se hace más proclive a errores cuando aumenta la complejidad.
En resumen, tomando las palabras de [5], la
interfaz de Java se puede comparar con aquel carro automático, en el cual se necesita saber poco
para poder conducirlo. En contraste, la interfaz
Pthreads de “C” luce como aquel carro sincrónico
que permite al conductor experto, sacar el mayor
provecho del vehı́culo cuando llegan las cuestas
pronunciadas u obtener la mejor arrancada. La interfaz Pthreads es más flexible, permite obtener
un mejor desempeño, pero su utilización resulta
compleja. El alto nivel y la flexibilidad reducida,
permite a Java tener ventaja cuando se desea intentar llevar el problema a otra plataforma u otra
arquitectura, ya que la máquina virtual de Java se
encarga de resolver el problema. Por su parte la
interfaz Pthreads, al ser de más bajo nivel, requiere que el programador haga modificaciones en el
código y/o la compilación para mantener un buen
desempeño de la aplicación al cambiar de plataforma o sistema operativo.
Como conclusiones particulares se deben
resaltar las siguientes:
Los tiempos de ejecución para los programas elaborados en Pthreads y Java (caso agente viajero)[?], permitieron constatar
que “C” no fue tan rápido (3 o más veces) como se proclama en varios sitios de
la Internet[7], [15], [10]. Por su lado, Java,
hasta el momento, no parece ser más rapido
que “C”, aunque en las nuevas versiones de
Java se prometen mejoras en los tiempos de
ejecución de programas multihilos[9].
Cuando se usa Pthreads es más fácil sincronizar utilizando semáforos[3], pero con el
uso de los mutex se pueden obtener mejores tiempos de ejecución. Esta condición se
verificó cuando se implementó el programa
del agente viajero usando semáforos en lugar de mutex[3] y se midió que el tiempo de
ejecución con semáforos era mayor (1.01
veces) que en las versiones implementadas con semáforos.
Cuando se elaboran programas multihilos,
se deben repetir las pruebas infinidad de veces para prever cualquiera condición de carrera que pudiera alterar el resultado final
bajo condiciones excepcionales. Estas condiciones excepcionales se descubren después de múltiples corridas del programa con
diferentes juegos de datos de entrada. Esto se constató en las primeras versiones del
programa de la suma paralela, las cuales
“aparentemente” funcionaban bien en las
primeras corridas y mostraban errores después de realizar varias pruebas.
Referencias
[1] Mordechai (Moti) Ben-Ari. Principles of
Concurrent and Distributed Programming.
Prentice-Hall International, 1990.
[2] Peter Chapin.
Pthread Tutorial.
2002.
También disponible como
http://www.ecet.vtc.edu/ ∼pchapin/ pthreadTutorial.pdf.
[3] Jorge A. Castellanos D. Estudio comparativo de la interfaz Pthreads del lenguaje de programación “C” y el soporte para
programación multihilo de Java. Universidad de Carabobo. Facultad Experimental
de Ciencias y Tecnologı́a. Departamento de
Computación. Trabajo de Ascenso, 2003.
[4] René Dorta F. Implementación de la interfaz para programación paralela de Modula3 bajo el sistema operativo Mach. Universidad de Carabobo. Escuela de Ingenierı́a
Eléctrica. Trabajo de Ascenso, 1998.
[5] Edward Fowlkes and Takashi Shida.
Multithreaded programming.
1996.
http://userpages.umbc.edu/∼schmitt/331F96
/tshida1/thread.html.
[6] A. Silberschatz; J. Peterson; P. Galvin. Sistemas Operativos - Conceptos Fundamentales. Tercera Edición. Addison-Wesley
Iberoamericana, 1994.
[7] Eric Galyon. C++ vs Java Performance. Colorado State University, Computer
Science Deparment, 1998. También disponible como http://www.cs.colostate.edu/
∼ cs154/PerfComp/.
[8] Ruben Pinilla; Marisa Gil. Jvm: plataform independent vs. performance dependent. Operating Systems Review ACM
press, 37(2):44–55, April 2003.
[9] JAVAOLYMPUS.
Java Performance. 2003. También disponible como
http://www.javaolympus.com/java/ PerformanceDirectory.html.
[10] Richard P. Martin. C and C++ are being
replaced for many applications, but they
would never become completely obsolete.
Rutgers University - Computer Science
Deparment, 2003.
También disponible
como http://www.cs.rutgers.edu/ rmartin/teaching/
spring03/cs553/papers01/04.pdf.
[11] Patrick Naughton; Michael Morrison. The
Java Handbook. Osborne/McGraw-Hill,
1996.
[12] Herbert Shildt. Java2 Manual de Referencia. 4ta. Edición. McGraw-Hill de España,
2001.
[13] William Stallings. Sistemas Operativos Segunda edición. Prentice Hall, Madrid, 1997.
[14] Andrew S. Tanenbaum. Sistemas Operativos Modernos. Primera Edición. Prentice
Hall Hispanoamericana S. A., 1993.
[15] Alavoor Vasudevan. C++ Programming
HOW-TO. Rootshell Security - Unix Systems security resources, 2000. También disponible como http://beatbox.suidzer0.org/
docs/c++programming/C++ProgrammingHOWTO.html#toc1.