Programación Concurrente y Distribuída Estado Compartido, 2010 Camilo Rueda 1 1 Universidad Javeriana-Cali 9 de abril de 2010
Concurrencia con estado: introducción problema: modelar con puertos un objeto contador. Sus operaciones son: Acceder al valor del contador cambiar el valor del contador Luego: programar un procedimiento que incrementa el contador
Concurrencia con estado: introducción problema: modelar con puertos un objeto contador. Sus operaciones son: Acceder al valor del contador cambiar el valor del contador Luego: programar un procedimiento que incrementa el contador proc {Inc C} N in {S C acceder(n)} {S C asignar(n + 1)}
Concurrencia con estado: introducción problema: modelar con puertos un objeto contador. Sus operaciones son: Acceder al valor del contador cambiar el valor del contador Luego: programar un procedimiento que incrementa el contador No funciona! proc {Inc C} N in {S C acceder(n)} {S C asignar(n + 1)}
Celdas: requisitos Evitar que se desordenen los accesos con las asignaciones Combinar asignación y acceso en una sola operación Intercambio atómico: {Exchange C X Y }
Nuevo concepto: Celda Crear una celda C con valor inicial V {NewCell V C} Consultar el valor de la celda C {Access C} @C Asignar a la celda un valor W {Assign C W } C := W intercambiar el valor de la celda con un valor nuevo W {Exchange C V W} Con celdas se trá estado explícito
Celdas: modelo
Dificultades Razonar sobre los programas es difícil: Muchas operaciones atómicas por hilo Número enorme de intercalamientos posibles Sean dos hilos con k acciones atómicas cada uno Número de intercalaciones: ( ) 2k k Controlar la complejidad: Abstracciones que agrupan acciones en una sola macroacción atómica.
El Modelo < s >::= skip < s1 > < s2 > stmt vacío y secuencia local x in s creación de var. x1 = x2 x = v Var-var, var-val binding if x then s1 else s2 Condicional case x of.... Pattern matching {x y 1...y n } applicación de procedimiento thread < s > creacion de un hilo try < s > 1 catch < x >... contexto para excepciones raise < x > notificar excepción {NewCell < x > < y > } crear celda {Excahange < x > < y > } intercambiar celda
Semántica de crear celda ({NewCell < x > < y > }, E) Crear un nombre nuevo n ligar E(< y >) con n en el store si lo anterior tiene éxito, agregar E(< y >) : E(< x >) al store mutable si no tiene éxito, señalar error
Semántica de intercambiar celda ({Exchange < x > < y > < z >}, E) si E(< x >) está determinado: si E(< x >) no es una celda, señale error si el store mutable contiene E(< x >) : w actualice el store mutable a E(< x >) : E(< z >) ligue E(< y >) con w en el store si E(< x >) no está determinado: suspa la ejecución
Un ejemplo declare fun {NewPila} Pila = {NewCell nil} proc {Push X} S in {Exchange Pila S X S} fun {Pop} X S in {Exchange Pila X S S} X in pila(push : Push pop : Pop)
Celdas vs Puertos La semática de NewPort y de NewCell es la misma! Pueden programarse puertos con celdas?
Celdas vs Puertos La semática de NewPort y de NewCell es la misma! Pueden programarse puertos con celdas? fun {NewPort Stream} {NewCell Stream} proc {S P M} Antes Despues in {Exchange P Antes Despues} Antes = M Despues
Celdas vs Puertos La semática de NewPort y de NewCell es la misma! Pueden programarse puertos con celdas? mejor... fun {NewS Stream} C = {NewCell Stream} proc {S M} Antes Despues in {Exchange C Antes Despues} Antes = M Despues in S
Abstracciones Cerrojos (locks) locks reentrantes Monitores Transacciones entrar espera CERROJO MONITOR TRANSACCIÓN salir commit abort
Parntesis: listas de diferencia técnica para optimizar operaciones sobre listas una lista L se representa mediante dos listas L = dif (X Y ) Los elementos de L son los de X sin los de Y Y siempre es una variable sin instanciar Ejemplo: X = 3 4 7 5 3 W, Y = W representa la lista L = 3 4 7 5 3 nil Operaciones: insertar A en la lista L : Y = A S, L = dif (X S) borrar el primer elemento de L : X = A S, L = dif (S Y )
Ejemplo: cola concurrente con estado fun {ColaNueva} X C = {NewCell q(0 X X)} proc {Inserte X} N S E1 in q(n S X E1) = {Access C} {Assign C q(n + 1 S E1)} fun {Borre} N S1 E X in q(n X S1 E) = {Access C} {Assign C q(n 1 S1 E)} X in cola(inserte : Inserte borrar : Borre) cómo son los llamados? funciona?
Ejemplo: cola concurrente con estado fun {ColaNueva} X C = {NewCell q(0 X X)} proc {Inserte X} N S E1 in q(n S X E1) = {Access C} {Assign C q(n + 1 S E1)} fun {Borre} N S1 E X in q(n X S1 E) = {Access C} {Assign C q(n 1 S1 E)} X in cola(inserte : Inserte borrar : Borre) cómo son los llamados? funciona? no!
Locks: primitivas crear cerrojo: {NewLock L} Saber si algo es un cerrojo: {IsLock X} guardar una zona con un cerrojo: lock L then S
Ejemplo: cola concurrente usando locks fun {ColaNueva} X C = {NewCell q(0 X X)} L = {NewLock} proc {Inserte X} N S E1 in lock L then q(n S X E1) = {Access C} {Assign C q(n + 1 S E1)} fun {Borre} N S1 E X in lock L then q(n X S1 E) = {Access C} {Assign C q(n 1 S1 E)} X in cola(inserte : Inserte borrar : Borre)
Espacios de tuplas (Linda) Abstracción indepiente del lenguaje que se compone de: Multiconjunto de tuplas cuatro operaciones Escritura: {TS write(t )} Agrega tupla a TS Lectura {TS read(l T )} Espera hasta que TS tenga tupla con etiqueta L La devuelve en T y la elimina de TS Lectura sin bloqueo: {TS readnonblock(l T B)} Returna sin espera: B = false si no hay tupla con etiqueta L B = true y la tupla en T, en caso contrario
Propiedades Proporciona memoria direccionable por contenido: Las tuplas se identifican por su etiqueta Las lecturas se desacoplan de las escrituras: no hay comunicación entre lectores y escritores Funcionamiento: Creación TS = {New EspacioDeTuplas init}} operación {TS write(algo(1 2 3))}, etc.
Locks y TS: código (ver concurrenciadeestado.oz)
Limitaciones de locks Son mecanismos sin información (booleanos) Solamente permiten exclusión mutua No facilitan sincronización (ej. buffer acotado) Propensos a errores (deadlock) P1 : thread lock Q then {R1} lock S then {R2} P2 : thread lock S then {R3} lock Q then {R4}
Monitores Primitiva estructurada de programación concurrente Utilizable para Garantizar acceso exclusivo a recursos (como en locks) sincronización y comunicación entre hilos Encapsula Definición de un recurso Operaciones que lo manipulan con exclusividad
Operaciones de monitores son la única interfaz con el recurso Solamente una puede estar activa en cada momento Invocadores inactivos esperan en cola hilos en espera se activan explícitamente por operaciones de otros hilos
Primitivas await B S Si B es cierto, el proceso se suspe libera la cerradura entra en la cola de espera Si B es falso, ejecuta en exclusividad S En Oz : solo existe versión con B = true notify elimina algún hilo H de la cola de espera H entra a competir por la cerradura para continuar ejecución H retoma la ejecución en el punto en que fue suspido notifyall Notifica a todos los procesos en la cola de espera
Estructura En estilo Pascaloide: < Nombre >: monitor begin declaración de datos locales procedure < nombre > (< parametros >) begin cuerpo (ejecución exclusiva) ; Declaración de otros procedimientos begin Initialización de datos locales ;
Estructura(2) En estilo Mozart, un monitor es una clase: Construcciones: class Nombre attr atributos meth metodo Cuerpo con llamado a primitivas de monitor M = {NewMonitor} {M.wait} {M.notify} {M.notifyAll}
Programar con monitores cada método es sección crítica protegida con un candado el método tiene una guarda (como en await B...) la guarda se evalua dentro del candado si la guarda es verdadera, se entra al cuerpo del método si no, el hilo espera.
Monitores: código (ver monitores.oz)