Вы находитесь на странице: 1из 63

Programacin Concurrente

Tema 3: Comunicacin y sincronizacin


de memoria compartida
Grado en Ingeniera de
Sistemas Electrnicos

Los conceptos tericos de este tema han sido desarrollados


por la Dra. M del Mar Gallardo

ndice
Tipo de procesos
El problema de los jardines (Exclusin Mutua)
El problema del productor consumidor (Condiciones de
sincronizacin)
Solucin al problema del productor consumidor con espera activa
Solucin al problema de la exclusin mutua con espera activa para
dos procesos
El problema de la panadera
Correccin de un programa concurrente
Justicia
El problema de los lectores/escritores
El problema de los filsofos

Procesos vs Recursos
En un programa concurrente intervienen tres
tipos de entidades:
Entidades activas: modeladas como procesos
(hebras, tareas,)
Entidades pasivas: son recursos que necesitan los
procesos para realizar su trabajo
Con control de acceso: para acceder a algunos recursos
es necesario que se satisfagan ciertas condiciones de
seguridad, como por ejemplo la exclusin mutua.
Sin control de acceso: recursos a los que se puede
acceder en cualquier momento.
3

El problema de los jardines


Supn que hay un jardn al que un nmero arbitrario de
personas puede visitar
Al jardn se puede acceder a travs de dos puertas distintas
El problema consiste en conocer cuantas personas hay en el
jardn en cada momento
Jardn

Visitantes
Puerta1

Puerta2

El problema de los jardines


Cada puerta es simulada por un proceso, que se
ejecuta concurrentemente
Puerta1 || Puerta2

Una variable global entera representar en cada


momento el nmero total de personas que ha
entrado por cada una de las puertas
Jardn

Visitantes
Puerta1

Puerta2
5

El Problema de los jardines


#include <pthread.h>
#include <stdio.h>
//Variable global
long Visitantes=0;
void IncVsitantes() {
Visitantes=Visitantes+1;
}

void *HebraVisitas(void *argg ){


int i=0;
long *NumVisitas;

NumVisitas= (long *) argg;


for (i=0;i<*NumVisitas;i++) {
IncVsitantes();
}
}
6

El Problema de los jardines


int main () {
pthread_t th1,th2,th3,th4;
pthread_attr_t attr;
// Cada hebra visita n veces
long visi1=1000000;
long visi2=2000000;
long visi3=2000000;
long visi4=2000000;
pthread_attr_init(&attr);
printf("Creamos th1 con 1000000 visitas\n");
pthread_create(&th1,&attr,HebraVisitas,&visi1);
printf("Creamos th2 con 2000000 visitas\n");
pthread_create(&th2,&attr,HebraVisitas,&visi2);
printf("Creamos th3 con 2000000 visitas\n");
pthread_create(&th3,&attr,HebraVisitas,&visi3);
printf("Creamos th4 con 2000000 visitas\n");
pthread_create(&th4,&attr,HebraVisitas,&visi4);
pthread_join (th1,NULL);
pthread_join(th2,NULL);
pthread_join (th3,NULL);
pthread_join(th4,NULL);

Cual es el
comportamiento?
Es correcto?

printf("El numero de visitas totales=%ld\n", Visitantes);


printf("Fin\n");
return 0;
}

El problema de los jardines


La variable Visitantes es compartida por todas las hebras
Las llamadas a la funcin de incremento de visitantes por
parte de las hebras interfieren
Las nicas instrucciones atmicas son las instrucciones
mquina
visitantes

inc()

p1

void incVisitantes(){
load cont
cont++
inc 1
se compila en
}
store cont

inc()

p2

El problema de los jardines


El objeto visitantes es compartido por las hebras p1 y p2
Las llamadas al mtodo inc de visitantes por parte de p1 y
p2 se interfieren
Las nicas instrucciones atmicas son las instrucciones
mquina
visitantes

inc()

public void inc(){


load cont
cont++
inc 1
se compila en
}
store cont

inc()

Ejemplo de traza de ejecucin


cont = 0
p1: load cont
p1

p2

CPU
?
9

El problema de los jardines


El objeto visitantes es compartido por las hebras p1 y p2
Las llamadas al mtodo inc de visitantes por parte de p1 y
p2 se interfieren
Las nicas instrucciones atmicas son las instrucciones
mquina
visitantes

inc()

public void inc(){


load cont
cont++
inc 1
se compila en
}
store cont

inc()

Ejemplo de traza de ejecucin


cont = 0
p1: load cont
p1

p2

CPU
0
10

El problema de los jardines


El objeto visitantes es compartido por las hebras p1 y p2
Las llamadas al mtodo inc de visitantes por parte de p1 y
p2 se interfieren
Las nicas instrucciones atmicas son las instrucciones
mquina
visitantes

inc()

public void inc(){


load cont
cont++
inc 1
se compila en
}
store cont

inc()

Ejemplo de traza de ejecucin


cont = 0
p1: load cont

p2: load cont


p1

p2

CPU
0
11

El problema de los jardines


El objeto visitantes es compartido por las hebras p1 y p2
Las llamadas al mtodo inc de visitantes por parte de p1 y
p2 se interfieren
Las nicas instrucciones atmicas son las instrucciones
mquina
visitantes

inc()

public void inc(){


load cont
cont++
inc 1
se compila en
}
store cont

inc()

Ejemplo de traza de ejecucin


cont = 0
p1: load cont
p1

p2: load cont


p1: inc 1
p2

CPU
1
12

El problema de los jardines


El objeto visitantes es compartido por las hebras p1 y p2
Las llamadas al mtodo inc de visitantes por parte de p1 y
p2 se interfieren
Las nicas instrucciones atmicas son las instrucciones
mquina
visitantes

inc()

public void inc(){


load cont
cont++
inc 1
se compila en
}
store cont

inc()

Ejemplo de traza de ejecucin


cont = 0
p1: load cont
p1

p2: load cont


p2: inc 1
p2

CPU
1
13

El problema de los jardines


El objeto visitantes es compartido por las hebras p1 y p2
Las llamadas al mtodo inc de visitantes por parte de p1 y
p2 se interfieren
Las nicas instrucciones atmicas son las instrucciones
mquina
visitantes

inc()

public void inc(){


load cont
cont++
inc 1
se compila en
}
store cont

inc()

Ejemplo de traza de ejecucin


cont = 1
p1: load cont
p1

p2

p2:
p1:
p2:
p1:

load cont
inc 1
inc 1
store cont

CPU
1
14

El problema de los jardines


El objeto visitantes es compartido por las hebras p1 y p2
Las llamadas al mtodo inc de visitantes por parte de p1 y
p2 se interfieren
Las nicas instrucciones atmicas son las instrucciones
public void inc(){
mquina
load cont
cont++

visitantes

inc()

p1

inc()

p2

se compila en

inc 1
store cont

Ejemplo de traza de ejecucin

p1:
p2:
p1:
p2:
p1:
p2:

load cont
load cont
inc 1
inc 1
store cont
store cont

cont = 1

CPU
1
15

El problema de los jardines

Este problema muestra uno de los grandes problemas de la programacin


concurrente
Detectar cuando un recurso (cont) compartido necesita algn control de
acceso para asegurar su integridad
Debemos impedir que varios procesos utilicen simultneamente el recurso
para que no se produzcan interferencias como las descritas.
El cdigo de acceso al recurso en cada una de las hebras se denomina
seccin crtica
La no interferencia entre secciones crticas significa que su ejecucin no
debe solaparse en el tiempo
Las secciones crticas deben aparecer como cdigos atmicos para el
resto de los procesos
Cuando dos secciones crticas no se interfieren se dice que se ejecutan en
exclusin mutua.
Garantizar la exclusin mutua requiere sincronizar a los procesos
involucrados
Si P1 quiere ejecutar su seccin crtica cuando P2 la est ejecutando,
P1 debe esperar a que P2 termine y viceversa

16

El problema del productor/consumidor


Un proceso productor produce de forma ininterrumpida datos que
deben ser consumidos por otro proceso consumidor
En su versin ms simple el productor deja el dato producido en una
variable compartida, a la que accede el consumidor para extraer dicho
dato

hebras
productor

consumidor

variable compartida
17

Productor/consumidor: principal
int main () {
pthread_t th1,th2,th3,th4;
pthread_attr_t attr;
long veces=1000000;

pthread_attr_init(&attr);
printf("Creamos th1 con 1000000 producciones\n");
pthread_create(&th1,&attr,HebraProductora,&veces);
printf("Creamos th2 con 1000000 consumiciones\n");
pthread_create(&th2,&attr,HebraConsumidora,&veces);
pthread_join (th1,NULL);
pthread_join(th2,NULL);

return 0;
}

Se maneja variable compartida y las hebras


productor y consumidor
18

Productor/consumidor: Hebras
void *HebraProductora(void *argg ){
long i=0;
long *NumVeces,num;
NumVeces= (long *) argg;
for (i=0;i<*NumVeces;i++) {
num=random();
Set(num);
printf("Productor=%ld\n",i);
}
}

#include <pthread.h>
#include <stdio.h>
//Variable global que contiene el dato
long Dato=0;

void Set(long val) {


Dato=val;
}
long Read () {
return Dato;
}

void *HebraConsumidora(void *argg ){


long i=0;
long *NumVeces, valor;

NumVeces= (long *) argg;


for (i=0;i<*NumVeces;i++) {
valor=Read();
printf("Consumidor=%ld\n", valor);
}
}

19

Productor/consumidor: ejemplo de una


ejecucin
Consumidor 32
Consumidor 32
Consumidor 32
Consumidor 32
Consumidor 32
Consumidor 32
Consumidor 32
Consumidor 32
Consumidor 32
Consumidor 32
Productor 32
Productor 71
Productor 53
Productor 98
Productor 9
Productor 87
Productor 46
Productor 90
Productor 67
Productor 87

El consumidor ha consumido el mismo dato muchas


veces
El productor ha producido datos que el consumidor
no ha ledo
En este caso no tenemos un problema de exclusin
mutua porque el productor escribe sobre la variable y
el consumidor lee
Hay que imponer dos condiciones
1.
2.

El consumidor no puede extraer un dato hasta que no


se ha producido uno nuevo
El productor no puede almacenar un nuevo dato hasta
que no se haya ledo el anterior

Estas propiedades imprescindibles para que la


solucin al problema sea correcta se denominan
condiciones de sincronizacin
Por el momento, modelamos estas condiciones
utilizando bucles de espera activa

20

Acceso a los datos


//Variable global que contiene el dato
long Dato=0;
//Variable logica para alternar produccion consumicion
unsigned HayDato=0;
void Set(long val) {
while (HayDato) ;
Dato=val;
HayDato=1;
}
long Read () {
long v;
while (!HayDato) ;
v=Dato;
HayDato=0;
return v;
}

Aadimos una variable


Booleana para saber en
cada momento si hay un
nuevo dato
Bucle de espera activa, el
productor
espera a que no haya datos
para almacenar el siguiente
(condicin de sincronizacin 2)
Bucle de espera activa, el
consumidor espera a que haya
un dato para extraerlo
(condicin de sincronizacin 1)

21

Productor Consumidor revisado (salida)


Productor 58
Consumidor 58
Productor 49
Consumidor 49
Productor 48
Consumidor 48
Productor 90
Consumidor 90
Productor 14
Consumidor 14
Productor 93
Consumidor 93
Productor 35
Consumidor 35
Productor 16
Consumidor 16
Productor 24
Consumidor 24
Productor 58
Consumidor 58

Cada dato producido por el productor es


consumido por el consumidor
El consumidor no consume dos veces
ningn dato
Esta solucin no es del todo satisfactoria
puesto que la ejecucin de los procesos
est muy acoplada (realizan cada iteracin
de su cuerpo de forma sincronizada)
La solucin general utiliza un buffer
intermedio para desacoplar a los procesos

22

Exclusin mutua con espera activa


Tipo de solucin que buscamos
Hebra p1

Hebra p2

run(){
while (1){
preProtocolo1
SC1
postProtocolo1
SNC1
}
}

run(){
while (1){
preProtocolo2
SC2
postProtocolo2
SNC2
}
}

Requisito 1:
En cada momento, hay, a lo sumo, una hebra
ejecutando su seccin crtica

23

Exclusin mutua con espera activa:


primer intento
unsigned f[2] = {false,false};
void main(){

Void * Hebra (void * id)


int miId,otra;
miId = (int) *id;
otra = (miId+1)%2;

pthread_t th1,th2;
pthread_attr_t attr;
int id1, id2;
pthread_attr_init(&attr);

while (1) {
f[miId]=1;

pthread_create(&th1,&attr,Hebra,&id1);

while (f[otra]);

pthread_create(&th2,&attr,Hebra,&id2);
..

SC
f[miId]=0;

PosProt

SNC

Cada hebra utiliza una variable booleana f[0..1]


}

f[i] es true sii la hebra i quiere entrar en la SC


Antes de entrar en la SC, la hebra i mira a ver si
la otra hebra quiere entrar, y si es as se espera

PreProt

}
}

Cuando sale de su SC, la hebra i pone de nuevo


su variable booleana a false
24

Exclusin mutua con espera activa:


primer intento
Requisito1:
En cada momento, hay, a lo sumo, una hebra
ejecutando su seccin crtica
Esta solucin satisface el Requisito1
Para probarlo basta observar que
el proceso pi est ejecutando SCi sii f[i] es true

1. Cuando p0 entra en SC0, f[1] es false y, por lo tanto, p1 no est en SC1


2. Mientras que p0 est en su SC0, f[0] es true, y, por lo tanto, p1 no puede
entrar en SC1

25

Exclusin mutua con espera activa:


primer intento
Requisito1:
En cada momento, hay, a lo sumo, una hebra
ejecutando su seccin crtica
Sin embargo, esta solucin no es vlida porque los procesos pueden quedarse
bloqueados en sus respectivas instrucciones de espera activa
Ejemplo:
p0: f[0] = true
p1: f[1] = true
p0: f[1] == true?
p1: f[0] == true?
:::::::::::::

Esta situacin se denomina livelock


Nuestra solucin debe, por lo tanto, satisfacer
algn requisito adicional

Requisito2:
Ausencia de livelock. Si las dos hebras quieren entrar
en sus SC simultneamente, en algn momento,
alguna de ellas, debera poder hacerlo
26

Exclusin mutua con espera activa:


Segundo intento
Cambiamos el orden de las instrucciones en
el preprotocolo para evitar el livelock

Void * Hebra(void id) {


int miId,otra;
miId = (int) *id;
otra = (miId+1)%2;
while (1) {
while (f[otra]);
f[miId]=1;

Esta solucin no es correcta porque se viola


la exclusin mutua

SC

PreProt

Ejemplo:
f[miId]=0;

p0: f[1] == true? No


p1: f[0] == true? No
p0: f[0] = true
p1: f[1] = true
P0 est en su SC
P1 est en su SC
:::::::::::::

SNC
}

}
}

ERROR!!
27

Exclusin mutua con espera activa:


Tercer Intento
int turno = 0;
void main(){
pthread_t th1,th2;
pthread_attr_t attr;
int id1, id2;
pthread_attr_init(&attr);

Void * Hebra ( void *id) {


int miId,otra;
miId = (int) * id;
otra = (miId+1)%2;
while (1) {

while (turno == otra)


;

SC
pthread_create(&th1,&attr,Hebra,&id1);

turno = otra;

pthread_create(&th2,&attr,Hebra,&id2);
.
}

Para evitar el livelock, usamos una variable turno,


que toma los valores 0 o 1
Antes de entrar en su SC, cada hebra mira a ver si
le toca, si no espera
Cuando sale de la seccin crtica le pasa el turno a
la otra hebra
Esta solucin no es vlida porque las hebras
deberan poder entrar en cualquier momento en su
SC si sta no est ocupada

SNC
}
}
}

Requisito 3:
Si slo una de las hebras quiere entrar en su SC,
en algn momento debera poder hacerlo
28

Exclusin mutua con espera activa:


Solucin de Peterson
int turno = 0;
intf[2] = {0,0};
main () {
..
}

Void * Hebra ( void *id) {


int miId,otra;
miId = (int) *id;
otra = (miId+1)%2;
while (1) {
f[miId] = 1;
turno = otra;

Es una combinacin de los intentos


1 y 3. Cuando P0 quiere entrar en
SC0 lo indica poniendo f0 a true y
le pasa el turno a P1.
P1 hace lo mismo.

SC

SNC
}

}
}

29

Exclusin mutua con espera activa:


Solucin de Peterson
Antes de entrar en SC0, P0 espera si
P1 quiere entrar en SC1 y es su
turno.
Cuando sale de SC0, P0 pone f0 a
false.
P1 hace lo mismo.

unsigned f[2]={0,0};
int turno=0;
void *Hebra(void *argg ){
//int *arg=(int *)argg; Otra forma de hacer el
casting
//int miId=*arg;
int miId=*((int *)argg);
int otra=(miId+1)%2;
while (1) {
f[miId] = 1;
turno = otra;
while (f[otra] && turno==otra) ;
printf("SECCION CRITICA id=%d \n",miId);

f[miId] = 0;
printf("SECCION NO CRITICA id=%d\n",miId);

}
}

30

Exclusin mutua con espera activa:


Solucin de Peterson
P0

P1

while (1){
f[0] = 1;
turno = 1;
while (f[1] && turno == 1)
;
SC0
f[0] := 0;
SNC0
}

while (1){
f[1] = 1;
turno = 0;
while (f[0] && turno == 0)
;
SC1
f[1] := 0;
SNC1
}

R1: En cada momento, hay, a lo sumo, un proceso ejecutando su seccin crtica

Esta solucin satisface R1. Se prueba como en el Primer Intento.

31

Exclusin mutua con espera activa:


Solucin de Peterson
P0

P1

while (1){
f[0] = 1;
turno = 1;
while (f[1] && turno == 1)
;
SC0
f[0] := 0;
SNC0
}

while (1){
f[1] = 1;
turno = 0;
while (f[0] && turno == 0)
;
SC1
f[1] := 0;
SNC1
}

R1: En cada momento, hay, a lo sumo, un proceso ejecutando su seccin crtica


R2: Ausencia de livelock. Si las dos hebras quieren entrar en sus SC
simultneamente en algn momento, alguna de ellas, debera poder hacerlo
Esta solucin satisface R2. Si f[0] y f[1] son true, la variable turno decide a
quin le toca entrar.

32

Exclusin mutua con espera activa:


Solucin de Peterson
P0

P1

while (1){
f[0] = 1;
turno = 1;
while (f[1] && turno == 1)
;
SC0
f[0] := 0;
SNC0
}

while (1){
f[1] = 1;
turno = 0;
while (f[0] && turno == 0)
;
SC1
f[1] := 0;
SNC1
}

R1: En cada momento, hay, a lo sumo, un proceso ejecutando su seccin crtica


R2: Ausencia de livelock. Si las dos hebras quieren entrar en sus SC
simultneamente en algn momento, alguna de ellas, debera poder hacerlo
R3: Si slo uno de los procesos quiere entrar en su seccin crtica, en algn
momento debera poder hacerlo.
Esta solucin satisface R3.
Supongamos que P0 quiere entrar en SC0, y que P1 no quiere entrar en SC1.
En este caso, como P1 no quiere entrar en SC1, f[1] es falso, lo que
significa que la expresin f[1] &&(turno == 1) es falsa, y, por lo tanto,
P0 puede entrar en SC0
33

Exclusin mutua con espera activa:


Solucin de Peterson
P0

P1

while (1){
f[0] = 1;
turno = 1;
while (f[1] && turno == 1)
;
SC0
f[0] := 0;
SNC0
}

while (1){
f[1] = 1;
turno = 0;
while (f[0] && turno == 0)
;
SC1
f[1] := 0;
SNC1
}

La solucin de Peterson satisface una propiedad adicional


R4: Justicia. Si ambos procesos quieren entrar simultneamente, primero
lo hace uno (P1, por ejemplo) y luego lo hace el otro (P2, en este caso).
Supongamos que P0 y P1 quieren entrar en SC0 y SC1 simultneamente, entonces
f[0] y f[1] son ambos true. Si, por ejemplo, turno == 0, entonces P0 entra
en SC0, y P1 se queda esperando en el bucle de espera activa de su cdigo.
Supongamos que P0 sale de SC0 (f[0] es false), y quiere volver a entrar,
entonces al ejecutar su preprotocolo pone f[0] a true, y turno a 1, por lo
que l mismo se cierra el paso a SC0, siendo P1 el que puede continuar ahora
ejecutando SC1.
34

El algoritmo de la panadera (Lamport)


El algoritmo de la panadera resuelve el problema de la
exclusin mutua para N >= 2 procesos, utilizando
espera activa como mecanismo de sincronizacin
No es un algoritmo justo porque no trata a todos los
procesos del mismo modo
Enunciado:
Se supone que tenemos N clientes que desean ser
atendidos en una panadera. El dependiente representa el
recurso compartido que debe ser utilizado en exclusin
mutua por todos los clientes (no se puede atender a dos o
ms clientes simultneamente)

35

El algoritmo de la panadera (Lamport)


Ilustracin
El dependiente debe atender a
los clientes en exclusin
mutua. En la ilustracin,
la exclusin mutua se representa
con un local pequeo
en el que no cabe ms
de un cliente

dependiente

4
3

clientes

36

Estructura del Cdigo: Procesos


N (=15) clientes llegan a la
panadera y son atendidos en
exclusin mutua.
El empleado es el recurso
compartido, es decir, una entidad
pasiva, que no hace falta
implementar como hebra.

clientes
/*Preprotocolo*/
// el cliente id es atendido por el dependiente
/*Posprotocolo*/
// el cliente id sale de la panadera

37

El algoritmo de la panadera (Lamport)


Ilustracin
Para modelar la exclusin mutua
los clientes van
pidiendo su turno cuando
llegan a la panadera,
para entrar de forma ordenada

dependiente

4
3

cul es mi turno?

38

Estructura del Cdigo: turno


Para modelar el turno, usamos un array con N componentes
(una por hebra)
Para cada hebra id, turno[id] == 0 sii no quiere acceder a su
seccin crtica. Por eso, inicialmente todos los turnos estn a
0, que es como decir que todos los clientes estn fuera de la
panadera

39

Estructura del Cdigo: Siguiente


Cada cliente pide su turno, utilizando la funcin siguiente, que itera
por el array turno, y devuelve el mayor valor encontrado ms 1.

..
int siguiente(){
int i,max = 0;
for ( i = 0; i<N; i++){
if (max<turno[i]) max=turno[i];
}
return max+1;
}

40

El algoritmo de la panadera (Lamport)


Ilustracin
Cuando un cliente ser atendido
pide su turno al mtodo
siguiente

dependiente

siguiente

1
2

1
1

41

El algoritmo de la panadera (Lamport)


Ilustracin
Cuando un cliente ser atendido
pide su turno al mtodo
siguiente

dependiente

siguiente

2
4

2
1

42

El algoritmo de la panadera (Lamport)


Ilustracin
Dados dos clientes id y i, el
mtodo meToca comprueba
si el turno de id es anterior al
de i

Si una hebra ve que a otra le


toca antes que a ella espera.

me toca

no
4
Antes de ser atendido, cada proceso
debe ver si es su turno
Para ello, comprueba si va antes o
despus que el resto de los procesos
uno a uno.

0
1

43

Estructura del Cdigo: meToca


int N = 15;
int turno [N];
..
int siguiente(){ .. }
int meToca(int id,int i){
// devuelve true si el turno de id es anterior al de i
if (turno[i] > 0 && turno[i] < turno[id])
return 0;
else if (turno[i] == turno[id] && i < id)
return 0;
else
return 1;
}
void *Hebra(void *argg ){
int i,j;
int id=*((int *)argg);
.
turno[id] = siguiente();
for ( i = 0; i<N; i++)
while (!meToca(id,i)) ;
SCId
// el cliente id es atendido por el dependiente
turno[id] = 0;
// el cliente id sale de la panadera

La funcin meToca comprueba


qu proceso va antes.
El bucle de espera activa hace
que un proceso se bloquee si hay
otro proceso que debe ejecutar
su seccin crtica antes.
Cuando el proceso id va delante
del resto, puede ejecutar su
seccin crtica
Cuando termina la seccin crtica
pone su turno a 0.

}
}

44

El algoritmo de la panadera (Lamport)


Ilustracin
dependiente

siguiente

1
4
Es posible que siguiente de el
mismo turno a dos procesos
distintos, puesto que no se
ejecuta en EXCLUSIN MUTUA

45

El algoritmo de la panadera (Lamport)


Ilustracin
dependiente

siguiente

1
3

1
Es posible que siguiente de el
mismo turno a dos procesos
distintos, puesto que no se
ejecuta en EXCLUSIN MUTUA

Las dos iteraciones de siguiente


se intercalan

46

Estructura del Cdigo: meToca


int meToca(int id,int i){
// devuelve true si el turno de id es anterior al de i
if (turno[i] > 0 && turno[i] < turno[id])
return 0;
else if (turno[i] == turno[id] && i < id)
return 0;
else
return 1;
}

La funcin meToca le da prioridad


a la hebra 0 porque tiene menor
identificador,
pero an no hemos terminado

47

Estructura del Cdigo: Traza de error


..
int siguiente(){ .. }
int meToca(int id,int i){
// devuelve true si el turno de id es anterior al de i
if (turno[i] > 0 && turno[i] < turno[id])
return 0;
else if (turno[i] == turno[id] && i < id)
return 0;
else
return 1;
}

void *Hebra(void *argg){


**
(1) turno[id] = siguiente();
(2) for ( i = 0; i<N; i++)
while (!meToca(id,i)) ;
(3) SCId
// el cliente id es atendido por el dependiente
turno[id] = 0;
// el cliente id sale de la panadera
}
}
}

Instruccin

turno

Inicialmente

0 0 0

c[0] llama a
siguiente y
ejecuta hasta **

0 0 0

c[2] llama a
siguiente y
ejecuta hasta (2)

1 0 0

c[2] llama a
meToca(2,0),

meToca(2,4)
y entra en su SC

1 0 0

C[0] termina
instruccin 1,
almacena 1 en
turno[0]

1 0 0

c[0] llama a
meToca(0,0),
meToca(0,1),
meToca(0,2)

y entra en su SC

1 0 0

Accin

c[2] en
SC2

c[0] en
SC0
ERROR
48

Estructura del Cdigo: pidiendoTurno


Declaramos un array pidiendoTurno que guarda en cada
momento si un proceso est escogiendo su turno o no.
Inicialmente, todas sus componente estn a false.
Cada proceso indica, modificando este array, si est
cogiendo su turno
Antes de comprobar si otro proceso va antes o despus que
l, espera hasta que ha terminado de escoger su turno.

49

Panadera: Cdigo definitivo


#include <pthread.h>
#include <stdio.h>

void *Hebra(void *argg ){


int i,j;
int id=*((int *)argg);

#define MAXT 15

for (i = 0; i<100000; i++){


pidiendoTurno[id]=1;
turno[id] = siguiente();
pidiendoTurno[id]=0;
for (j = 0; j<MAXT; j++){
while (pidiendoTurno[j]) ;
while (!meToca(id,j)) ;
}
cont++;
turno[id] = 0;

int turno[MAXT];
int pidiendoTurno[MAXT];
int cont=0;
int siguiente(){
int max = 0, i;
for (i =0; i<MAXT; i++)
if (max<turno[i])
max=turno[i];
return max+1;
}

int meToca(int id,int i){


if (turno[i] > 0 &&
turno[i] < turno[id]) return 0;
else if (turno[i] == turno[id] &&
i < id) return 0;
else return 1;
}

}
}

void CreoId (long *id) {


int i=0;
for (i=0;i<MAXT;i++) {
id[i]=i;
}
}
50

Panadera: Cdigo definitivo


int main () {
pthread_t thread[MAXT];
pthread_attr_t attr;
long id[MAXT];
int i;
CreoId(id);
Inicializar(&turno);
pthread_attr_init(&attr);
for (i=0;i<MAXT;i++) {
pthread_create(&thread[i],&attr,Hebra,&id[i]);
}
for (i=0;i<MAXT;i++) {
pthread_join(thread[i],NULL);
}
return 0;

51

Correccin de un programa concurrente


Propiedades de seguridad: las que afirman que el
sistema nunca va a entrar en un estado malo o de
error

Exclusin mutua
Condiciones de sincronizacin para el productor/consumidor
Ausencia de bloqueo (deadlock)
Deadlock es el estado del sistema en el que todos los procesos estn
bloqueados esperando algn evento.

Propiedades de viveza: las que afirman que en algn


momento ocurre algo bueno en el sistema

R3: Si slo una hebra quiere entrar en su SC, en algn


momento debe poder hacerlo
R2: Si las dos hebras quieren entrar en su seccin crtica, en
algn momento alguna de ellas debe poder hacerlo
(ausencia de livelock)
R4:Ausencia de posposicin indefinida (starvation): todos los
procesos del sistema tienen la oportunidad de evolucionar
en su cdigo

52

Justicia (fairness)
La justicia del planificador afecta algunas
propiedades del sistema (las de viveza)
Planificador justo: aqul que asegura que
cualquier proceso que est en estado listo es
alguna vez seleccionado para continuar con su
ejecucin.
int fin1, fin2;

void *hebra(void *arg){

void *hebra2(void *arg){

fin1 = 1;

while (!fin1) ;

while (!fin2) ;

fin2 = 1;
}

}
}

Con un planificador justo este programa


terminara siempre.

53

Justicia
Planificador dbilmente justo: aqul que asegura que si un
proceso hace una peticin de forma continua, en algn
momento ser atendida.
Planificador fuertemente justo: aqul que asegura que si un
proceso hace una peticin con infinita frecuencia, en algn
momento ser atendida.
hebra1

hebra2

while (!fin2) {

while (!fin1) ;

fin1 = 1;

fin2 = 1;

fin1 = 0;
}

}
}

}
}
.

Con un planificador dbilmente justo este programa podra no terminar.


Con un planificador fuertemente justo, siempre termina.

54

Lectores/Escritores
El problema de los lectores/escritores
representa un modelo de sincronizacin entre
dos tipos de procesos (los lectores y los
escritores) que acceden a un recurso
compartido, tpicamente una base de datos
(BD).
Los procesos escritores acceden a la BD para
actualizarla.
Los procesos lectores leen los registros de la
BD.
55

Lectores/Escritores
Condicin de
sincronizacin para los
escritores:
Acceden a la BD en
exclusin mutua con
cualquier otro proceso
de tipo lector o escritor

l0 l1

l2

l3

l4

BD
e2

e1

e0

56

Lectores/Escritores
Condicin de
sincronizacin para los
lectores:
Cualquier nmero de
lectores puede acceder
simultneamente a la
BD.

l1

l2

BD
l0

e1

l3

e0

l4

e2

57

Lectores/Escritores: PseudoCdigo
Lector {

Escritor {
int id;

int id;
while (1){
entraLector(id);
//lector id en la BD
saleLector(id);
}

while (1){
entraEscritor(id);
//escritor id en la BD
saleEscritor(id);
}

}
}
void main(){
pthread_t Lectores[MAXL];
pthread_t Escritores [MAXE];
for (int i = 0; i<MAXL; i++)
Creo hebras Lectoras;
for (int i = 0; i<MAXE; i++)
Creo hebras Escritoras
}

La BD no hace falta modelarla


Todos los lectores ejecutan los mismos protocolos
de entrada y salida

Todos los escritores ejecutan los mismos


protocolos de entrada y salida

58

El problema de los filsofos


N = 5 procesos filsofos dedican su vida a dos
nicas tareas:
pensar, la mayor parte del tiempo
comer, de vez en cuando
void * Filosofo (void *arg){

while (1){
//pensar
//comer
}
}

59

El problema de los filsofos

En el comedor hay una mesa en la


que cada filsofo tiene su puesto.
En el centro de la mesa hay una
cantidad ilimitada de comida (fideos
chinos, espaguettis,)
Adyacentes al plato que corresponde
a cada filsofo, hay dos tenedores
que el filsofo necesita para poder
comer

Platn
Descartes

La tarea pensar representa la


actividad que cada proceso
puede hacer sin necesidad de
sincronizarse ni comunicarse con
los dems.
Sin embargo, para comer los
filsofos tienen que ponerse de
acuerdo:

60

El problema de los filsofos


As que el cdigo para comer para
cada filsofo es:
Platn
Descartes

{coge tenedores izdo y dcho}


{come}
{devuelve tenedores izdo y dcho}

61

El problema de los filsofos


As que el cdigo para comer para
cada filsofo es:
Platn
Descartes

{coge tenedores izdo y dcho}


{come}
{devuelve tenedores izdo y dcho}

Este sistema nos sirve para representar


-La exclusin mutua
-Condiciones de sincronizacin
-Deadlock
-PostPosicin Indefinida
-.

62

Referencias
Concurrency: State Models & Java Programs
Jeff Magee, Jeff Kramer, Ed. Willey
Concurrent Programming
Alan Burns, Geoff Davies, Ed. Addison Wesley

63

Вам также может понравиться