Práctica 12 - ProgProcesosYServicios/Practicas-2 GitHub Wiki
Práctica 12: Exclusión mutua "atómica"
La práctica 2.1 que motivó el estudio de la exclusión mutua estaba hecha de tal manera que pudiera conseguirse condiciones de carrera fácilmente incluso en sistemas monoprocesador. Sin embargo, el método estático sumaN no tiene ningún sentido más allá de intentar separar el instante de lectura de la variable que se va a modificar y su escritura con el nuevo valor.
En esta práctica vamos a retocar el programa para hacer desaparecer el método sumaN ()
de modo que haremos la suma con una única expresión aritmética normal (aunque dentro del for externo). Esto volverá a hacer difícil sufrir condiciones de carrera en sistemas monoprocesador, pero tendrá una programación mucho más razonable.
1.- Haz una copia del proyecto de la práctica 2.1 y dale por nombre ExclusionMutuaAtomica
. Renombra la clase principal y ponle ese mis-
mo nombre.
2.- Elimina el método estático sumaN ()
) y modiñca el método run()
por algo mucho más simple:
public void run() {
for (int i = 1; i <= NUM_VECES; ++i)
_suma += NUMERO_SUMADO;
} // run
3.- Observa que la operación de suma puede ocasionar condiciones de carrera, porque no es atómica. En primer lugar es necesario leer de memoria el valor del atributo _suma, luego realizar la operación y finalmente llevar el resultado de nuevo a memoria.
4.- Ejecuta la aplicación en un solo procesador (taskset
o start / affinity
). ¿Funciona bien?
5.- Ejecuta la aplicación sobre un sistema multiprocesador. ¿Funciona bien?
Como se ha dicho) es muy poco probable que en un monoprocesador el programa falle (aunque la posibilidad existe). Fue por eso por lo que originalmente hicimos el método sumarN
, para aumentar las posibilidades (y
poder añadir un yield()
si era necesario). En un sistema multiprocesador, por otro lado, es muy poco probable que funcione bien.
Como siempre, necesitamos hacer uso del recurso compartido _suma
en exclusión mutua. Pero dentro de la sección crítica lo único que vamos a tener es una suma. El tiempo de “negociación” a la entrada de la sección crítica) y la
liberación al final es mucho más costoso que las tres instrucciones (máquina) de la suma.
En realidad lo que queremos es una simple suma, pero realizada de forma atómica. Podríamos ahorramos la sección crítica si el “hardware” nos diera la posibilidad de hacer una suma de manera atómica, igual que hace con la instrucción Test and Set.
La clase AtomicInteger
proporciona, además del método ya conocido compareAndSet()
, otros para realizar operaciones sencillas pero que, en condiciones normales) no serían atómicas. La clase nos garantiza la atomicidad que no tendríamos si hiciéramos la operación con una expresión.
En esta práctica vamos a sustituir el atributo long _suma por un objeto de la clase AtomicLong, y usaremos el método addAndGet()
.
1.- Modifica el código de la práctica para que el atributo _suma sea un objeto de la clase java.util.concurrent.atomic.LongInteger.
2.- Modifica el método get()
para que devuelva el valor long almacenado en el nuevo objeto _suma.
3.- Modifica el método run() para que en lugar de hacer la suma manual mente se utilice el método addAndGet()
de la clase AtomicLong
.
4.- Ejecuta la aplicación en un sistema multihebra. ¿Funciona?