Vous êtes sur la page 1sur 28

Introducción

Hasta este punto se han visto las estructuras de datos presentadas por Arreglos,
Pilas y Colas, éstas son denominadas estructuras estáticas, porque durante la
compilación se les asigna un espacio de memoria, y éste permanece inalterable
durante la ejecución del programa. En la siguiente unidad se muestra la estructura de
datos “Lista”. La cual es un tipo de estructura lineal y dinámica de datos. Lineal
debido a que a cada elemento le puede seguir sólo otro elemento, y dinámica porque
se puede manejar la memoria de manera flexible, sin necesidad de reservar espacio
con anticipación.

Una de las principales ventajas de manejar un tipo dinámico es que se pueden


obtener posiciones de memoria a medida que se va trabajando con el programa, y
éstas se liberan cuando ya no se requiere, de ese modo se crea una estructura más
dinámica que cambia dependiendo de la necesidad, según se agreguen o eliminen
elementos.

Lo anterior solucionaría en gran manera el manejo de los espacios en memoria,


necesarios para la solución de problemas y así optimizar el uso de los recursos del
sistema, es importante destacar que las estructuras dinámicas no pueden reemplazar a
los arreglos en todas sus aplicaciones. Hay casos numerosos que podrían ser
solucionados, de modo fácil, aplicando arreglos, en tanto que si se utilizaran
estructuras dinámicas, como las listas, la solución de tales problemas se complicaría
Pila (informática).

Una pila (stack en inglés) es una lista ordinal o estructura de datos en la que el
modo de acceso a sus elementos es de tipo LIFO (del inglés Last In First Out, último
en entrar, primero en salir) que permite almacenar y recuperar datos. Se aplica en
multitud de ocasiones en informática debido a su simplicidad y ordenación implícita
en la propia estructura. Para el manejo de los datos se cuenta con dos operaciones
básicas: apilar (push), que coloca un objeto en la pila, y su operación inversa, retirar
(o desapilar, pop), que retira el último elemento apilado. En cada momento sólo se
tiene acceso a la parte superior de la pila, es decir, al último objeto apilado
(denominado TOS, Top of Stack en inglés). La operación retirar permite la obtención
de este elemento, que es retirado de la pila permitiendo el acceso al siguiente (apilado
con anterioridad), que pasa a ser el nuevo TOS. Por analogía con objetos cotidianos,
una operación apilar equivaldría a colocar un plato sobre una pila de platos, y una
operación retirar a retirarlo.

Las pilas suelen emplearse en los siguientes contextos:


 Evaluación de expresiones en notación postfija (notación polaca
inversa).
* Reconocedores sintácticos de lenguajes independientes del contexto
* Implementación de recursividad.

Operaciones

Una pila cuenta con 2 operacines imprescindibles: apilar y desapilar, a las que en
las implementaciones modernas de las pilas se suelen añadir más de uso habitual.
 Crear: se crea la pila vacía.
 Apilar: se añade un elemento a la pila.(push)
 Desapilar: se elimina el elemento frontal de la pila.(pop)
 Cima: devuelve el elemento que esta en la cima de la pila. (top o peek)
 Vacía: devuelve cierto si la pila está vacía o falso en caso contrario.

Operaciones con Pilas


 Creación de pila

crearPila (nombrePila)

 Comprobación del estado


pilaVacia(nombrePila) → Booleano
pilaLlena(nombrePila) → Booleano

 Inserción de nodos

push(nombrePila, valorInfo)

 Extracción de nodos

pop(nombrePila) → información

 Acceso a la cabecera

cabecera(nombrePila) → informacion

 Acceso a los nodos

info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → Enlace

 Modificación de los nodos

asignarInfo(referenciaNodo, valorInformacion)
asignarEnlace(referenciaNodo, valorEnlace)

Pilas alojadas en arreglos


Implementación de pilas con arreglos: resulta conveniente el almacenamiento
secuencial para el tratamiento de pilas. Podemos tener una variable llamada TOPE
que contenga el índice donde está ubicado el tope de la pila. Cuando la pila está
vacía, se hace TOPE=0.
Por ejemplo: se puede definir una constante llamada CANTELEMENTOS que
tenga el valor máximo de elementos que podrá contener la pila, un tope con la última
posición, un valor a ser ingresado y el arreglo mismo:
#define CANTELEMENTOS 10;
int tope= -1; /*inicializamos a la variable tope con el valor -1 para que represente
a la pila vacía porque el 1º elemento tiene índice 0.*/
int pila[CANTELEMENTOS];
int valor;
Para darte un ejemplo sencillo: supone que tienes una pila de 10 platos. El
primer plato de la pila será el último en quitarse y el último plato de la pila será el
primero en quitarse.
Un arreglo constituye el depósito de los elementos de la pila. El rango del
arreglo debe ser lo suficientemente amplio para poder contener el máximo previsto de
elementos de la pila. Un extremo del arreglo se considera el fondo de la pila, que
permanecerá fijo. La parte superior de la pila, tope o cima, estará cambiando
dinámicamente durante la ejecución del programa. Además del arreglo, una variable
entera nos sirve para tener en todo momento el índice del arreglo que contiene el
elemento tope. Las declaraciones, procedimientos y funciones para representar el
TAD pila, forman parte de la unidad pila.
· Max_elem=…(Dependerá de cada realización).
· Tipo_elem=…(Tipo de los elementos de la pila).
· Pila= Registro.
· Elem= arreglo [1…Max_elem] de Tipo_elem;
· Tope= entero; FinRegistro.

Implementación de Procedimientos Recursivos mediante pilas


Un procedimiento o función contiene tanto variables locales como argumentos
ficticios o parámetros. A través de los argumentos se transmiten datos en las llamadas
a los subprogramas, o bien, se devuelven valores al programa o subprograma
invocante. Además, el subprograma debe guardar la dirección de retorno al programa
que realiza la llamada. En el momento en que termina la ejecución de un
subprograma el control pasa a la dirección guardada.
Ahora, el subprograma es recursivo, entonces además de la dirección de retorno,
los valores actuales de las variables locales y argumentos deben de guardarse ya que
se usarán de nuevo cuando el subprograma se reactive. Supongamos que se ha
llamado al subprograma P, que tiene llamadas a si mismo, es decir, es recursivo. El
funcionamiento del programa recursivo P será: ü se crea una pila para cada
argumento. ü se crea una pila para cada variable local. ü se crea una pila para
almacenar la dirección de retorno. Cada vez que se hace una llamada recursiva a P,
los valores actuales de los argumentos y de las variables locales se meten en sus pilas
para ser procesadas posteriormente. Asimismo, cada vez que hay un retorno a P
procedente de una llamada recursiva anterior, se restauran los valores de variables
locales y argumentos de la simas de la pilas. Para la obtención de la dirección de
retorno es de suponer que el procedimiento P contiene una llamada recursiva en la
sentencia N. Entonces guarda en optra pila la dirección de retorno, que será la
sentencia siguiente, la N+1. De tal forma que cuando el nivel de ejecución del
procedimiento P actual termine, o sea, alcance la sentencia end final, usará dicha pila
de direcciones para volver al nuevo nivel de ejecución.

COLAS
Una cola (también llamada fila) es una estructura de datos, caracterizada por ser
una secuencia de elementos en la que la operación de inserción push se realiza por un
extremo y la operación de extracción pop por el otro. También se le llama estructura
FIFO (del inglés First In First Out), debido a que el primer elemento en entrar será
también el primero en salir.

Representación de las Colas


Se las puede representar por listas enlazadas o por arrays
C= Q (1), Q (2),…, Q (n).
En cualquier caso se necesitan dos punteros
frente (f)
final(r)
y la lista o array de n elementos (LONGMAX)

Las operaciones que pueden realizar con una cola son:


· Acceder al primer elemento de la cola.
· Añadir un elemento al final de la cola.
· Eliminar el primer elemento de la cola.
· Vaciar una cola.
· Verificar el estado de la cola: vacía o llena.

Operaciones
· Crear: se crea cola vacía.
· Encolar (añadir, entrar, insertar): se añade un elemento a la cola. Se
añade al final de esta.
· Desencolar (sacar, salir, eliminar): se elimina el elemento frontal de la
cola, es decir, el primer elemento que entró.
· Frente (consultar, front): se devuelve el elemento frontal de la cola, es
decir, el primer elemento que entró.
Operaciones con Cola
 Creación de la cola

crearCola (nombreCola)

 Comprobación del estado

ColaVacia(nombreCola) → Booleano
ColaLlena(nombreCola) → Booleano

 Inserción de nodos

encolar(nombreCola, valorInfo)

 Extracción de nodos

desencolar(nombrePila) → informacion

 Acceso a la cabecera

cabecera(nombrePila) → informacion

 Acceso a los nodos

info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → Enlace

 Modificación de los nodos

asignarInfo(referenciaNodo, valorInformacion)
asignarEnlace(referenciaNodo, valorEnlace)

Cola Circulares
Una cola circular o anillo es una estructura de datos en la que los elementos están
de forma circular y cada elemento tiene un sucesor y un predecesor. Los elementos
pueden cosultarse, añadirse y eliminarse únicamente desde la cabeza del anillo que es
una posición distinguida. Existen dos operaciones de rotaciones, una en cada sentido,
de manera que la cabeza del anillo pasa a ser el elemento sucesor, o el predecesor,
respectivamente, de la cabeza actual.

Colas Doblemente Enlazadas


Como ocurre con toda representación estática, una de las principales desventajas
es que hay que prever un máximo de elementos, y de ese máximo no se puede pasar.
La realización de una cola mediante una lista enlazada permite ajustarse exactamente
al número de elementos de la cola. Esta implementación con las listas enlazadas
utiliza dos punteros que acceden a la lista, el puntero frente y el puntero final. Cola
implementada con listas enlazadas, el puntero final referencia al último nodo que fue
añadido. En esta representación no tiene sentido la operación que indica si la cola está
llena. Al ser una estructura dinámica crece y decrece según las necesidades. La
definición de cola usando listas enlazadas serían:
· Tipo_elto: (Tipo del campo información de cada nodo).
· Nodo: Registro.
· Elto: Tipo_elto;
· Sig: hNodo;
· Fregistro; Cola:
· Registro frente: hNoho;
· final: hNodo; Fregistro;

Tipos de Colas
a) Colas Circulares (anillos): en las que el último elemento y el primero están
unidos.
b) Colas de Prioridad: En ellas, los elementos se atienden en el orden
indicado por una prioridad asociada a cada uno. Si varios elementos tienen la misma
prioridad, se atenderán de modo convencional según la posición que ocupen. Hay 2
formas de implementación:
a. Añadir un campo a cada nodo con su prioridad. Resulta conveniente
mantener la cola ordenada por orden de prioridad.
b. Crear tantas colas como prioridades haya, y almacenar cada elemento en su
cola.
c) Bicolas: son colas en donde los nodos se pueden añadir y quitar por ambos
extremos; se les llama DEQUE (Double Ended QUEue). Para representar las Bicolas
se puede hacer con un array ciruclar con inicio y fin que apunten a cada uno de los
extremos. Hay variantes:
a. Bicolas de entrada restringida: son aquellas donde la inserción sólo se hace
por el final, aunque se puede eliminar al inicio ó al final.
b. Bicolas de salida restringida: son aquellas donde sólo se elimina por el
final, aunque se puede insertar al inicio y al final.

Aplicaciones de Colas
Las colas también se utilizan en muchas maneras en los sistemas operativos para
planificar el uso de los distintos recursos de la computadora. Uno de estos recursos es
la propia CPU (Unidad Central de Procesamiento).
Si esta trabajando en un sistema multiusuario, cuando le dice a la computadora
que ejecute un programa concreto, el sistema operativo añade su petición a su “cola
de trabajo”. Cuando su petición llega al frente de la cola, el programa solicitado pasa
a ejecutarse. Igualmente, las colas se utilizan para asignar tiempo a los distintos
usuarios de los dispositivos de entrada / salida (E/S), impresoras, discos, cintas y
demás. El sistema operativo mantiene colas para peticiones de imprimir, leer o
escribir en cada uno de estos dispositivos.

Listas. Definición

Las listas no son arreglos (arrays), aunque ambos representan secuencias de


elementos de un tipo, los arreglos tienen longitud fija; las listas, no; es decir, las listas
son flexibles y permiten cambio de implementación.
En una lista enlazada, cada elemento apunta al siguiente excepto el último que no
tiene sucesor y el valor del enlace es null. Por ello los elementos son registros que
contienen el dato a almacenar y un enlace al siguiente elemento. Los elementos de
una lista, suelen recibir también el nombre de Nodos de la lista.

Tipos de listas

Listas simples

Se definen como un conjunto de nodos uno detrás de otro, del cual siempre se
puede conocer al nodo inicial y al final, de cada nodo de la lista, se conoce un
contenido, que es la información que almacena dentro puede ser de cualquier tipo de
dato un sucesor único excepto el ultimo nodo de la lista.

Operaciones de las listas simples


 Creación de una lista

crearLista (nombreLista)

 Comprobación del estado


listaVacia(nombreLista) → Booleano
listaVacia(referenciaNodo) → Booleano
listaLlena(nombreLista) → Booleano

 Inserción de nodos

insertar(nombreLista, valorInfo, posición)


insertar(nombreLista, valorInfo)

 Borrado de nodos

borrar(nombreLista, valorInfo)

 Búsqueda de un nodo

buscar(nombreLista, dato)→información
buscar(nombreLista, dato)→referenciaNodo
pertenece(nombreLista,informacion) → Booleano

 Recorrido de la lista

recorrer(nombreLista)

 Acceso a los nodos

info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → enlace

 Modificación de nodos

asignarInfo(referenciaNodo, valorInformacion)
asignarEnlace(referenciaNodo, valorEnlace)

 Acotación sobre las operaciones

ListaLlena sólo tiene sentido si lista es acotada.


Acceso y modificación serán métodos get y put.

Listas ordenadas
Son las que la posición de cada nodo viene determinada por el valor de uno o
más campos obligatorios de información del nodo denominados clave No se permite
tener dos nodos con la misma clave.

Operaciones de las listas ordenadas


 Creación de una lista

crearLista (nombreLista)

 Comprobación del estado

listaVacia(nombreLista) → Booleano
listaVacia(referenciaNodo) → Booleano
listaLlena(nombreLista) → Booleano

 Insesrción de nodo

insertar(nombreLista, valorClave, valorInfo)

 Borrado de nodos

borrar(nombreLista, valorClave)

 Búsqueda de un nodo

buscar(nombreLista, valorClave) → Informacion


buscar(nombreLista, valorClave)→ReferenciaNodo
pertenece(nombreLista,valorClave) → Booleano

 Acceso a los nodos

clave(referenciaNodo) → Clave
info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → enlace

 Modificación de los nodos


asignarClave(referenciaNodo, valorClave)
asignarInfo(referenciaNodo, valorInformacion)
asignarEnlace(referenciaNodo, valorEnlace)

Implementación de listas encadenadas, métodos de inserción. Búsqueda

La lista enlazada es un TDA que nos permite almacenar datos de una forma
organizada, al igual que los vectores pero, a diferencia de estos, esta estructura es
dinámica, por lo que no tenemos que saber "a priori" los elementos que puede
contener.

En una lista enlazada, cada elemento apunta al siguiente excepto el último que no
tiene sucesor y el valor del enlace es null. Por ello los elementos son registros que
contienen el dato a almacenar y un enlace al siguiente elemento. Los elementos de
una lista, suelen recibir también el nombre de nodos de la lista.

struct lista {
gint dato;
lista *siguiente;
};

Representa el dato a almacenar. Puede ser de cualquier tipo; en este ejemplo


se trata de una lista de enteros.

Es un puntero al siguiente elemento de la lista; con este puntero enlazamos


con el sucesor, de forma que podamos construir la lista.

Esquema de un nodo y una lista enlazada.


Para que esta estructura sea un TDA lista enlazada, debe tener unos operadores
asociados que permitan la manipulación de los datos que contiene. Los operadores
básicos de una lista enlazada son:

 Insertar: inserta un nodo con dato x en la lista, pudiendo realizarse esta


inserción al principio o final de la lista o bien en orden.
 Eliminar: elimina un nodo de la lista, puede ser según la posición o por
el dato.

 Buscar: busca un elemento en la lista.

 Localizar: obtiene la posición del nodo en la lista.

 Vaciar: borra todos los elementos de la lista

Después de esta breve introducción, que sólo pretende servir como recordatorio,
pasaremos a ver cómo es la estructura GSList que, junto con el conjunto de funciones
que la acompañan, forman el TDA lista enlazada en GLib™.

GSList

La definición de la estructura GSList o, lo que es lo mismo, un nodo de la lista,


está definido de la siguiente manera:

struct GSList {
gpointer data;
GSList *next;
};

Representa el dato a almacenar. Se utiliza un puntero genérico por lo que


puede almacenar un puntero a cualquier tipo de dato o bien almacenar un entero
utilizando las macros de conversión de tipos.

Se trata de un puntero al siguiente elemento de la lista.

Las macros de conversión disponibles son las siguientes:

 GINT_TO_POINTER ()
 GPOINTER_TO_INT ()

 GUINT_TO_POINTER ()
 GPOINTER_TO_UINT ()

Más adelante, en esta misma sección, se verán ejemplos del uso de estas macros.

Las funciones que acompañan a la estructura GSList y que implementan los


operadores básicos de las listas enlazadas, son las siguientes:

Operadores de inserción en listas enlazadas.

Operador Funciones asociadas a GSList.

Insertar al GSList* g_slist_prepend (GSList *list, gpointer data)


principio.

Insertar al final. GSList* g_slist_append (GSList *list, gpointer data)

Insertar en la GSList* g_slist_insert (GSList *list, gpointer data,


posición indicada. gint position)

Insertar en orden. GSList* g_slist_insert_sorted (GSList *list, gpointer


data, GCompareFunc func)

Las funciones de inserción al principio de la lista, g_slist_prepend, y al


final, g_slist_append, son sencillas de usar. Sólo hay que pasarles como parámetros la
lista donde queremos añadir el dato así como el dato a insertar y la función devuelve
una lista con el nuevo dato insertado.

La función g_slist_insert inserta el dato en la posición indicada. Su uso también


es sencillo como puede verse en el siguiente ejemplo.

Insertar un nuevo dato en una posición determinada.

/* obtiene el numero de nodos de la lista */


length = g_slist_length (list);

g_print ("\nEscribe el nº de indice donde se insertara el dato (el indice maximo


es %d): ", length);
scanf ("%d", &index);

/* inserta el valor en la posicion indicada */

if (index < length) {


list = g_slist_insert (list, GINT_TO_POINTER (value), index);
print_list (list);
}

En este ejemplo se utiliza la función g_slist_length para obtener el número de


nodos que contiene la lista. A esta función hay que pasarle como parámetro la lista de
la que se desea obtener el número de nodos y devuelve como resultado el número de
nodos de ésta.

guint * g_slist_length ( list );


GSList * list ;

La función g_slist_insert_sorted inserta los elementos a la lista de forma


ordenada. Esta función utiliza el parámetro GCompareFunc para insertar el dato en la
posición correcta.

GCompareFunc es una función que se utiliza para comparar dos valores y saber
así cual de ellos hay que insertar primero. En los dos ejemplos que hay a
continuación, se puede observar una función de tipo GCompareFunc y su uso para
insertar datos en una lista en orden creciente.

Parámetro GCompareFunc para insertar en orden creciente.

gint compare_value1 (gconstpointer a, gconstpointer b) {


gint *value1 = (gint *) a;
gint *value2 = (gint *) b;

return value1 > value2;


}

Insertar elementos en orden creciente.

gint values[] = {8, 14, 5, 12, 1, 27, 3, 13};


gint i;
/* insertando valores en orden creciente */
for (i = 0; i < 8; i++) {
list = g_slist_insert_sorted (list, GINT_TO_POINTER (values[i]),
compare_value1);
}

Tabla 7. Operadores de eliminación en listas enlazadas.


Operador Funciones asociadas a GSList.

Eliminar un nodo. GSList* g_slist_remove (GSList *list,


gconstpointer data)

Eliminar nodos según GSList* g_slist_remove_all (GSList *list,


un patrón. gconstpointer data)

Las dos funciones expuestas para la eliminación de nodos, si bien tienen una
definición prácticamente idéntica, el resultado obtenido es distinto. En el caso
de g_slist_remove, se eliminará el nodo que contenga el valor data. Si hay varios
nodos con el mismo valor, sólo se eliminará el primero. Si ningún nodo contiene ese
valor, no se realiza ningún cambio en el GSList. En el caso de g_slist_remove_all, se
eliminan todos los nodos de la lista que contengan el valor data y nos devuelve la
nueva lista resultante de la eliminación de los nodos.

Operadores de búsqueda en listas enlazadas.

Operador Funciones asociadas a GSList.

Buscar un nodo según GSList* g_slist_find (GSList *list, gconstpointer


un valor. data)

Buscar un nodo según GSList* g_slist_find_custom (GSList *list,


un criterio. gconstpointer data, GCompareFunc func)

Localizar el índice de un GSList* g_slist_index (GSList *list,


nodo. gconstpointer data)

Localizar la posición de GSList* g_slist_position (GSList *list, GSList


un nodo. *llink)

Obtener el último nodo. GSList* g_slist_last (GSList *list)

Obtener el siguiente g_slist_next (slist)


nodo.

Obtener un nodo por su GSList* g_slist_nth (GSList *list, guint n)


posición.

Obtener el dato de un gpointer g_slist_nth_data (GSList *list, guint n)


nodo según su posición.

Todas estas funciones, a excepción de g_slist_nth_data, devuelven un nodo de la


lista o NULL si el elemento no existe. La función g_slist_nth_datadevuelve el valor
del elemento según la posición que se le pasa como argumento en el parámetro n o
NULL si la posición que se le pasa está más allá del final de la lista.

La función g_slist_next, es una macro que nos devuelve el siguiente nodo. Esta
macro la podemos utilizar para recorrer la lista.

Ejemplo 18. Función que imprime una lista.

void print_list (GSList *list) {

gint i = 0;

while (list != NULL) {


g_print ("Node %d content: %d.\n", i, list->data);

/* apunta al siguiente nodo de la lista */


list = g_slist_next (list);
i++;
}
}

Operador para vaciar la lista.

Funciones asociadas
Operador
a GSList.

void g_slist_free (GSList


Vacía la lista y libera la memoria usada.
*list)

La función g_slist_free libera la memoria de la lista que se le pasa como


parámetro.

Con estas funciones, quedan definidos los operadores básicos del TDA lista
enlazada. GSList trae otras funciones además de los operadores básicos. Para más
información sobre estas, está disponible el manual de referencia de GLib™.

Manejo de las Excepciones

Una excepción es la indicación de que se produjo un error en el programa. Las


excepciones, como su nombre lo indica, se producen cuando la ejecución de
un método no termina correctamente, sino que termina de manera excepcional
como consecuencia de una situación no esperada.

Cuando se produce una situación anormal durante la ejecución de un programa


(por ejemplo se accede a un objeto que no ha sido inicializado o tratamos de
acceder a una posición inválida en un vector), si no manejamos de manera
adecuada el error que se produce, el programa va a terminar abruptamente su
ejecución. Decimos que el programa deja de funcionar y es muy probable que el
usuario que lo estaba utilizando ni siquiera sepa qué fue lo que pasó.

Cuando durante la ejecución de un método el computador detecta un error,


crea un objeto de una claseespecial para representarlo (de la clase Exception en
Java), el cual incluye toda la información del problema, tal como el punto del
programa donde se produjo, la causa del error, etc. Luego, "dispara" o "lanza"
dicho objeto (throw en inglés), con la esperanza de que alguien lo atrape y decida
como recuperarse del error. Si nadie lo atrapa, el programa termina, y en la consola
de ejecución aparecerá toda la información contenida en el objeto que representaba
el error. Este objeto se conoce como una excepción. En el ejemplo 1 se ilustra esta
idea.

Ejemplo 1

Objetivo: Dar una idea global del concepto de excepción.

Este ejemplo ilustra el caso en el cual durante la ejecución de un método se


produce un error y el computador crea un objeto para representarlo y permitir que
en alguna parte del programa alguien lo atrape y lo use para evitar que el programa
deje de funcionar.

public class C1
{
private C2 atr;

public void m1( )


{
atr.m2( );
}
}
 Suponga que tenemos una clase C1, en la cual hay un método llamado
m1(), que es llamado desde las clases de la interfaz del programa.
 Los objetos de la clase C1 tienen un atributo de la clase C2, llamado atr.

 Suponga además que dentro del método m1() se invoca el método m2() de
la clase C2 sobre el atributo llamado atr.

public class C2
{
public void m2( )
{
instr1;
instr2;
instr3;
}
}
 Dentro de la clase C2 hay un método llamado m2() que tiene 3
instrucciones, que aquí mostramos como instr1, instr2, instr3. Dichas instrucciones
pueden ser de cualquier tipo.
 Suponga que se está ejecutando la instrucción instr2 del método m2() y se
produce un error. En ese momento, a causa del problema el computador decide que
no puede seguir con la ejecución del método (instr3 no se va a ejecutar).
 Crea entonces un objeto de la clase Exception que dice que el error sucedió
en la instrucción instr2 del método m2() y explica la razón del problema.
 Luego, pasa dicho objeto al método m1() de la clase C1, que fue quien hizo
la llamada. Si él lo atrapa (ya veremos más adelante cómo), el computador
continúa la ejecución en el punto que dicho método indique.
 Si el método m1() no atrapa la excepción, este objeto pasa a la clase de la
interfaz que hizo la llamada. Este proceso se repite hasta que alguien atrape
la excepción o hasta que el programa completo se detenga. Entendemos por
manejar una excepción el hecho de poderla identificar, atraparla antes de que el
programa deje de funcionar y realizar una acción para recuperarse del error (por lo
menos, para informarle al usuario lo sucedido de manera amigable y no con un
mensaje poco comprensible del computador).

Multithreading

En la arquitectura de computadoras, el subprocesamiento múltiple es la


capacidad de una unidad central de procesamiento (CPU) (o un solo núcleo en un
procesador de múltiples núcleos) para proporcionar múltiples subprocesos de
ejecución al mismo tiempo, admitidos por el sistema operativo. Este enfoque difiere
del multiprocesamiento. En una aplicación multiproceso, los subprocesos comparten
los recursos de uno o varios núcleos, que incluyen las unidades informáticas, las
memorias caché de la CPU y el búfer de traducción (TLB).

Cuando los sistemas de multiprocesamiento incluyen varias unidades de


procesamiento completas en uno o más núcleos, el multiproceso pretende aumentar la
utilización de un solo núcleo mediante el uso de paralelismo de nivel de subproceso,
así como el paralelismo de nivel de instrucción. Como las dos técnicas son
complementarias, a veces se combinan en sistemas con múltiples CPU de
subprocesamiento múltiple y con CPU con múltiples núcleos de subprocesamiento
múltiple.

Visión general
El paradigma de subprocesos múltiples se ha vuelto más popular a medida que
los esfuerzos para explotar aún más el paralelismo a nivel de instrucción se han
estancado desde finales de los años noventa. Esto permitió que el concepto de
computación de rendimiento resurgiera del campo más especializado del
procesamiento de transacciones. A pesar de que es muy difícil acelerar un solo
subproceso o un solo programa, la mayoría de los sistemas informáticos son en
realidad multitarea entre varios subprocesos o programas. Por lo tanto, las técnicas
que mejoran el rendimiento de todas las tareas resultan en ganancias de rendimiento
general..

Ventajas
Si un subproceso obtiene una gran cantidad de errores de caché, los otros
subprocesos pueden continuar aprovechando los recursos informáticos no utilizados,
lo que puede llevar a una ejecución general más rápida, ya que estos recursos estarían
inactivos si solo se ejecutara un único subproceso. Además, si un subproceso no
puede utilizar todos los recursos informáticos de la CPU (porque las instrucciones
dependen del resultado de cada uno), ejecutar otro subproceso puede evitar que esos
recursos se vuelvan inactivos.

Desventajas
Varios subprocesos pueden interferir entre sí cuando se comparten recursos de
hardware, como cachés o buffers de traducción (TLB). Como resultado, los tiempos
de ejecución de un solo hilo no se mejoran y se pueden degradar, incluso cuando solo
se está ejecutando un hilo, debido a las frecuencias más bajas o etapas de canalización
adicionales que son necesarias para acomodar el hardware de conmutación de hilos.

La eficiencia general varía; Intel reclama una mejora de hasta un 30% con su
tecnología Hyper-Threading, [1] mientras que un programa sintético que simplemente
realiza un ciclo de operaciones de punto flotante dependientes no optimizadas en
realidad gana una mejora de velocidad del 100% cuando se ejecuta en paralelo. Por
otro lado, los programas de lenguaje ensamblador ajustados a mano que utilizan las
extensiones MMX o AltiVec y realizan la búsqueda previa de datos (como un buen
codificador de video) no sufren pérdidas de caché o recursos informáticos inactivos.
Por lo tanto, tales programas no se benefician de los subprocesos múltiples de
hardware y, de hecho, pueden ver un rendimiento degradado debido a la disputa por
los recursos compartidos.

Tipos de multihilo

Entrelazado / Temporal multihilo


Multihilo de grano grueso

El tipo más simple de subprocesos múltiples se produce cuando un hilo se ejecuta


hasta que es bloqueado por un evento que normalmente crearía un bloqueo de larga
latencia. Tal bloqueo podría ser una falla de caché que tiene que acceder a la memoria
fuera del chip, lo que podría requerir cientos de ciclos de CPU para que los datos
regresen. En lugar de esperar a que se resuelva el bloqueo, un procesador de hilos
cambiaría la ejecución a otro que estaba listo para ejecutarse. Solo cuando los datos
del hilo anterior hubieran llegado, el hilo anterior se volvería a colocar en la lista de
subprocesos listos para ejecutar.

Por ejemplo:

Ciclo i: se emite la instrucción j del hilo A.


Ciclo i + 1: se emite la instrucción j + 1 del hilo A.
Ciclo i + 2: se emite la instrucción j + 2 del subproceso A, que es una instrucción
de carga que se pierde en todos los cachés.
Ciclo i + 3: programador de hilos invocado, cambia al hilo B.
Ciclo i + 4: se emite la instrucción k del hilo B.
Ciclo i + 5: se emite la instrucción k + 1 del hilo B.
Conceptualmente, es similar a la multitarea cooperativa utilizada en sistemas
operativos en tiempo real, en la que las tareas renuncian voluntariamente al tiempo de
ejecución cuando necesitan esperar algún tipo de evento. Este tipo de multihilo se
conoce como multihilo de bloque, cooperativo o de grano grueso.

El objetivo del soporte de hardware de subprocesos múltiples es permitir el


cambio rápido entre un hilo bloqueado y otro hilo listo para ejecutarse. Para lograr
este objetivo, el costo del hardware es replicar los registros visibles del programa, así
como algunos registros de control del procesador (como el contador del programa).
Cambiar de un hilo a otro significa que el hardware cambia de usar un conjunto de
registros a otro; para cambiar de manera eficiente entre subprocesos activos, cada
subproceso activo debe tener su propio conjunto de registros. Por ejemplo, para
cambiar rápidamente entre dos subprocesos, el hardware de registro debe ser
instanciado dos veces.

Entrelazado multihilo

El propósito de los subprocesos múltiples intercalados es eliminar todos los


bloqueos de dependencias de datos del proceso de ejecución. Dado que un subproceso
es relativamente independiente de otros subprocesos, hay menos posibilidades de que
una instrucción en una etapa de canalización necesite una salida de una instrucción
anterior en la tubería. Conceptualmente, es similar a la multitarea preventiva utilizada
en los sistemas operativos; una analogía sería que el intervalo de tiempo dado a cada
subproceso activo es un ciclo de CPU.
Por ejemplo:

Ciclo i + 1: se emite una instrucción desde el hilo B.


Ciclo i + 2: se emite una instrucción desde el hilo C.
Este tipo de multihilo se llamó primero procesamiento de barril, en el que los
pentagramas de un barril representan las etapas de la tubería y sus hilos de ejecución.
El multihilo entrelazado, preventivo, de grano fino o en intervalos de tiempo es una
terminología más moderna.

Además de los costos de hardware descritos en el tipo de bloque de


subprocesamiento múltiple, el subproceso entrelazado tiene un costo adicional de
cada etapa de canalización que sigue el ID de subproceso de la instrucción que está
procesando. Además, dado que hay más subprocesos que se ejecutan
simultáneamente en la canalización, los recursos compartidos, como las memorias
caché y los TLB, deben ser más grandes para evitar la agitación entre los diferentes
subprocesos.
Conclusiones
De lo anteriormente expuesto se concluye que; una Pila es una colección
ordenada de elementos en la que se pueden insertar y suprimir por un extremo,
llamado tope. Por tal razón se conoce como una estructura de datos LIFO (last-in,
firstout)
Las listas son estructuras lineales y dinámicas de datos. La principal ventaja del
dinamismo lo representa el hecho de que se adquieren posiciones de memoria a
medida que se necesitan y se liberan cuando ya no se requieren. Es decir, se llegan a
expandir o contraer, dependiendo de la aplicación. El dinamismo de estas estructuras
soluciona el problema de decidir cuánto espacio se necesita a priori, por ejemplo, en
una estructura de datos estática como el arreglo. En este capítulo estudiaremos las
listas lineales, circulares y doblemente ligadas. También se presentan estas estructuras
con un enfoque orientado a objetos.
En la práctica para la comprensión del manejo de excepciones en threads, tema
de alta importancia debido a que su entendimiento nos permite construir
aplicaciones multithreading tolerante a fallas lo que conlleva a la construcción de
software más robusto y de nivel de diseño industrial.

Bibliografía

Adam Drozdek, Estructura de datos y algoritmos en java, México, Thomson


Learning, 2000.
Alfred V. Aho / Jonh E. Hopcroft, Estructura de datos y algoritmos, Madrid, Pearson
Educación, 1983.
Antonio Garrido / Joaquin Fernández, Abstracción y estructuras de datos en c++,
Madrid, Delta publicaciones 2006.
Goodrich / Tamassia, Estructura de datos y algoritmos en java, México, CECSA,
2002.
Harvey M. y Paul J. Deitel, Como programar en java, México, Deitel, 2004
Anexos

Vous aimerez peut-être aussi