O documento discute processos concorrentes e sincronização. Aborda conceitos como comunicação entre processos, problemas de sincronização, seções críticas e mecanismos de sincronização como semáforos. Apresenta exemplos de código Java para ilustrar buffer circular entre produtor e consumidor e discute soluções para evitar condições de corrida.
2. Processos Concorrentes
Comunicação Interprocessos
O Problema da Seção Crítica
Sincronização por Hardware e por Software
Semáforos
Problemas Clássicos de Sincronização
Eduardo Nicola F. Zagari
2
Comunicação e Sincronização
3. Processos que executam ao mesmo tempo podem existir de
maneira independente um dos outros ou podem requerer
sincronização ocasional ou cooperação.
Processos Independentes versus Processos Cooperantes
Independentes: não afetam nem são afetados pela execução de um
outro processo
Cooperantes: podem afetar ou serem afetados pela execução de
outros processos
Vantagens da cooperação:
compartilhamento de informação
aumento da velocidade de computação
modularidade
conveniência
Eduardo Nicola F. Zagari
3
Comunicação e Sincronização
4. Paradigma de processos cooperantes:
producer produz informação que é consumida pelo consumer
Buffer sem limite
Buffer limitado
Sincronização
Processo Processo
Produtor Consumidor
dados dados
buffer
tamanho ilimitado x limitado
Eduardo Nicola F. Zagari
4
Comunicação e Sincronização
5. public interface Buffer
{
// producers call this method
public abstract void insert(Object
item);
// consumers call this method
public abstract Object remove();
}
Eduardo Nicola F. Zagari
5
Comunicação e Sincronização
6. import java.util.*;
public class BoundedBuffer implements
Buffer
{
private static final int BUFFER SIZE = 5;
private int count; // number of items in
the buffer
private int in; // points to the next
free position
private int out; // points to the next
full position
private Object[] buffer;
public BoundedBuffer() {
// buffer is initially empty
count = 0;
in = 0;
out = 0;
buffer = new Object[BUFFER SIZE];
}
// producers calls this method
public void insert(Object item) {
// Slide 7
}
// consumers calls this method
public Object remove() {
Eduardo Nicola F. Zagari
// Slide 8 6
Comunicação e Sincronização
}
7. public void insert(Object item) {
while (count == BUFFER SIZE)
; // do nothing -- no free buffers
// add an item to the buffer
++count;
buffer[in] = item;
in = (in + 1) % BUFFER SIZE;
}
Eduardo Nicola F. Zagari
7
Comunicação e Sincronização
8. public Object remove() {
Object item;
while (count == 0)
; // do nothing -- nothing to consume
// remove an item from the buffer
--count;
item = buffer[out];
out = (out + 1) % BUFFER SIZE;
return item;
}
Eduardo Nicola F. Zagari
8
Comunicação e Sincronização
9. Mecanismo do SO que permite que processos se comuniquem e
sincronizem suas ações
Sistema de Mensagens – processos se comunicam entre si sem
a necessidade de compartilhar variáveis
IPC fornece duas operações:
send(message) – mensagem de tamanho fixo ou variável
receive(message)
Se P e Q desejam se comunicar, eles precisam:
Estabelecer um canal de comunicação entre eles
Trocar mensagens via send/receive
Implementação do canal de comunicação:
físico (p.ex.: memória compartilhada)
lógico (p.ex.: propriedades lógicas)
Eduardo Nicola F. Zagari
9
Comunicação e Sincronização
10. Como a ligação é estabelecida?
Pode ser associada a mais de 2 processos?
Quantas ligações podem existir entre 2 processos?
Qual a capacidade?
Tamanho da mensagem fixo ou variável?
Unidirecional ou bidirecional?
Eduardo Nicola F. Zagari
10
Comunicação e Sincronização
11. Comunicação direta x indireta
mailbox
send(P, mensagem)
send(A, mensagem)
receive(Q, mensagem)
receive(A, mensagem)
ligação automática
ligação entre processos que
ligação entre 2 processos
compartilham A
só existe uma ligação
ligação entre 2 ou mais processos
uni ou bidirecional
podem existir várias ligações
desvantagem: se mudar o ID do uni ou bidirecional
processo?
Processos têm que compartilhar o
mailbox (ID único)
Eduardo Nicola F. Zagari
11
Comunicação e Sincronização
12. Troca de Mensagens pode ser bloqueante ou não-bloqueante
Bloqueante é considerada síncrona
send bloqueante – quem envia espera até que uma mensagem
seja recebida
receive bloqueante – quem recebe espera até que uma
mensagem esteja disponível
Não-bloqueante é considerada assíncrona
send não-bloqueante – o transmissor envia a mensagem e
continua
receive não-bloqueante – o receptor recebe uma mensagem
válida ou um null
Eduardo Nicola F. Zagari
12
Comunicação e Sincronização
13. Filas de mensagens implementam uma das três capacidades:
1. Zero – 0 mensagens
não pode haver mensagem esperando, quem envia deve esperar
até que a mensagem seja recebida (sincronizada)
2. Limitada – número finito de n mensagens
se está cheio, quem envia deve esperar
5. Ilimitada – tamanho infinito
quem envia nunca espera
Eduardo Nicola F. Zagari
13
Comunicação e Sincronização
14. Acesso concorrente a dados compartilhados podem resultar
em inconsistência dos mesmos
Manter a consistência dos dados requer mecanismos para
assegurar a execução ordenada dos processos cooperantes
Race condition : situação em que dois ou mais processos
querem partilhar um recurso (escrever e ler dados
compartilhados) e o resultado final depende de quem executou
quando (ordem de escalonamento)
Processos concorrentes necessitam de mecanismos de
sincronização para evitar que outro processo tenha acesso a
uma variável comum, quando esta está sendo modificada por
um processo
Eduardo Nicola F. Zagari
14
Comunicação e Sincronização
15. solução de Shared-memory para o problema do buffer limitado
A
tem uma condição de corrida na classe de dados count.
Produtor
Consumidor
while (1) {
while (1) {
while (count ==BUFFER_SIZE)
while (count == 0)
; // não faz nada
; // não faz nada
// produz um item e o coloca
nextConsumed = buffer[out];
em nextProduced
out = (out + 1) % BUFFER_SIZE;
buffer[in] = nextProduced;
count--;
in = (in + 1) % BUFFER_SIZE;
// consome o item em nextConsumed
count++;
}
}
Eduardo Nicola F. Zagari
15
Comunicação e Sincronização
17. Como evitar condições de corrida?
Evitar que os processos leiam e escrevam dados compartilhados ao
mesmo tempo
Exclusão Mútua: se um processo estiver usando uma variável ou
arquivo, o outro processo está excluído do uso
Isto é, garantia de acesso exclusivo a um recurso
Parte do tempo o processo executa computação que não leva a
condições de corrida a parte do programa em que memória
compartilhada é acessada é chamada de Região Crítica ou
Seção Crítica
Eduardo Nicola F. Zagari
17
Comunicação e Sincronização
18. 1. Exclusão Mútua – Se o processo Pi está executando dentro da sua
seção crítica, então nenhum outro processo pode estar executando
em sua seção crítica correspondente
Dois processos não podem estar simultaneamente dentro de suas regiões
críticas correpondentes
3. Progresso – Se nenhum processo está executando dentro de sua
seção crítica e existem alguns processos que desejam adentrar suas
seções críticas, então a seleção do próximo processo que irá entrar
em sua seção crítica não pode ser adiada
Nenhum processo que esteja rodando fora de sua seção crítica pode
bloquear a execução de outro processo
5. Espera Limitada - Nenhum processo pode ser obrigado a esperar
indefinidamente para entrar em sua região crítica
Nenhuma suposição pode ser feita com relação à velocidade de execução
dos processos ou ao número de processadores disponíveis no sistema
Eduardo Nicola F. Zagari
18
Comunicação e Sincronização
19. Mecanismos de Hardware:
Inibição de Interrupções
Instrução TSL (Test and Set Lock)
Mecanismos de Software:
Com Espera Ocupada:
Variáveis de Travamento (Lock Variables)
Alternância Estrita
Solução de Peterson
Sem Espera Ocupada:
Dormir e Acordar (Sleep and Wakeup)
Semáforos
Contadores de Eventos
Monitores
Troca de Mensagens
Eduardo Nicola F. Zagari
19
Comunicação e Sincronização
20. Solução mais simples
Desabilitam-se todas as interrupções após se entrar na região
crítica ao sair, reabilitam-nas
Se as interrupções não ocorrem, o processo não pode sofrer
preempção
Problema 1: ... e se o processo não reabilitar as interrupções?
Problema 2: se o sistema tem 2 CPUs e somente uma tem as
interrupções inibidas, a outra pode acessar a memória
compartilhada
Por outro lado, é muito conveniente que o kernel possa
desabilitar as interrupções para atualizar variáveis ou listas
Conclusão: solução adequada para o kernel, mas não para
processos de usuário
Eduardo Nicola F. Zagari
20
Comunicação e Sincronização
21. Instrução especial que permite ler uma variável, armazenar seu
conteúdo em uma outra área e atribuir um novo valor a esta
variável (hardware)
É uma instrução indivisível: executada sem interrupção
Variável compartilhada flag: quando flag = 0, qualquer processo
pode fazê-la igual a 1 (instrução TSL)
Eduardo Nicola F. Zagari
21
Comunicação e Sincronização
22. enter_region:
tsl register, flag | copia flag p/
| registrador e faz flag = 1
cmp register, #0 | o flag é zero?
jnz enter_region | se não,lock e setado;loop
ret | retorna, entrou na R.C.
leave_region:
mov flag, #0 | guarda um 0 em flag
ret | retorna a quem chamou
Eduardo Nicola F. Zagari
22
Comunicação e Sincronização
23. Define-se uma única variável compartilhada, inicialmente com
valor zero
Se um processo deseja entrar na região crítica, ele testa a
variável. Se ela for 1 (recurso liberado), ela é feita 0 (recurso
trancado) e o processo entra. Caso contrário, o processo
aguarda até que o valor da variável seja 1 (um)
Eduardo Nicola F. Zagari
23
Comunicação e Sincronização
24. x = 0, recurso trancado
x = 1, recurso liberado
while (x == 0) do /* nada */; Entrada
x = 0; Entrada
... Região Crítica
x = 1; Saída
Problema: Este mecanismo apresenta a falha de que a alteração
de valor para “trancado”ʼ, após o teste, permite que dois processos
executem a Região Crítica (RC) ao mesmo tempo
O teste e a alteração necessitam ser feitos de forma indivisível
(atômica)
Eduardo Nicola F. Zagari
24
Comunicação e Sincronização
25. Processo A Processo B
while (TRUE) { while (TRUE) {
while (turn != 0); while (turn != 1);
Reg_Crítica(); Reg_Crítica();
turn = 1; turn = 0;
Reg_Não_Crítica(); Reg_Não_Crítica();
} }
Inicialmente a variável turn é feita igual a 0 (zero)
Assim, o Processo A consegue entrar na região crítica
Enquanto isto, o Processo B ficará continuamente testando a
variável (Espera Ocupada)
Eduardo Nicola F. Zagari
25
Comunicação e Sincronização
26. Problema 1: o fato do Processo B ficar fazendo “Espera
Ocupada” deve ser evitado, pois consome tempo de CPU
Problema 2: não é uma boa solução quando um processo é
muito mais lento que o outro
Problema 3: viola a condição de que um processo que não
esteja em sua região crítica não bloqueie outro
Eduardo Nicola F. Zagari
26
Comunicação e Sincronização
27. Antes de entrar na Região Crítica (RC), cada
processo chama enter_region com seu no (0 ou
1) como parâmetro. Ao sair, leave_region
#include “prototypes.h”
#define FALSE 0
#define TRUE 1
#define N 2 /* no de processos */
int turn; /* de quem é a vez */
int interested[N]; /* todos valores inici- */
/* almente 0 (FALSE) */
Eduardo Nicola F. Zagari
27
Comunicação e Sincronização
28. void enter_region (int process) { /* process = quem
está entrando (0 ou 1) */
int other; /* no do outro processo */
other = 1 - process; /* o outro processo */
interested[process] = TRUE; /* mostra interesse */
turn = process; /* define flag */
while (turn==process && interested[other]==TRUE);
}
void leave_region (int process) { /* process = quem
está saindo */
interested[process] = FALSE; /* indica saída RC */
}
Eduardo Nicola F. Zagari
28
Comunicação e Sincronização
29. Duas tarefas, T0 e T1 (Ti e Tj)
Três “soluções” são apresentadas. Todas elas implementam a
interface MutualExclusion :
public interface MutualExclusion
{
public static final int TURN_0 = 0;
public static final int TURN_1 = 1;
public abstract void enteringCriticalSection(int turn);
public asbtract void leavingCriticalSection(int turn);
}
Eduardo Nicola F. Zagari
29
Comunicação e Sincronização
30. Usado para criar duas threads e testar cada algoritmo
public class AlgorithmFactory
{
public static void main(String args[]) {
MutualExclusion alg = new Algorithm_1();
Thread first = new Thread( new Worker("Worker 0", 0, alg));
Thread second = new Thread(new Worker("Worker 1", 1, alg));
first.start();
second.start();
}
}
Eduardo Nicola F. Zagari
30
Comunicação e Sincronização
31. public class Worker implements Runnable
{
private String name;
private int id;
private MutualExclusion mutex;
public Worker(String name, int id, MutualExclusion mutex) {
this.name = name;
this.id = id;
this.mutex = mutex;
}
public void run() {
while (true) {
mutex.enteringCriticalSection(id);
MutualExclusionUtilities.criticalSection(name);
mutex.leavingCriticalSection(id);
MutualExclusionUtilities.nonCriticalSection(name);
}
}
}
Eduardo Nicola F. Zagari
31
Comunicação e Sincronização
32. Threads compartilham uma variável inteira comum (turn)
Se turn==i, thread i é permitda a executar
Não satisfaz o requisito “Progresso”
Por quê?
Eduardo Nicola F. Zagari
32
Comunicação e Sincronização
33. public class Algorithm_1 implements MutualExclusion
{
private volatile int turn;
public Algorithm_1() {
turn = TURN_0;
}
public void enteringCriticalSection(int t) {
while (turn != t)
Thread.yield();
}
public void leavingCriticalSection(int t) {
turn = 1 - t;
}
}
Eduardo Nicola F. Zagari
33
Comunicação e Sincronização
34. Adicionar mais informação de estado
Flags booleanas para indicar o interesse das threads em
entrar na seção crítica
O requisito “Progresso” ainda não é satisfeito
Por quê?
Eduardo Nicola F. Zagari
34
Comunicação e Sincronização
35. public class Algorithm_2 implements MutualExclusion
{
private volatile boolean flag0, flag1;
public Algorithm_2() {
flag0 = false; flag1 = false;
}
public void enteringCriticalSection(int t) {
if (t == 0) {
flag0 = true;
while(flag1 == true)
Thread.yield();
}
else {
flag1 = true;
while (flag0 == true)
Thread.yield();
}
}
// Continued On Next Slide
Eduardo Nicola F. Zagari
35
Comunicação e Sincronização
37. Combina as idéias de 1 e 2
Ele satisfaz os requisitos da solução do problema da seção
crítica?
Eduardo Nicola F. Zagari
37
Comunicação e Sincronização
38. public class Algorithm_3 implements MutualExclusion
{
private volatile boolean flag0;
private volatile boolean flag1;
private volatile int turn;
public Algorithm_3() {
flag0 = false;
flag1 = false;
turn = TURN_0;
}
// Continued on Next Slide
Eduardo Nicola F. Zagari
38
Comunicação e Sincronização
39. public void enteringCriticalSection(int t) {
int other = 1 - t;
turn = other;
if (t == 0) {
flag0 = true;
while(flag1 == true && turn == other)
Thread.yield();
}
else {
flag1 = true;
while (flag0 == true && turn == other)
Thread.yield();
}
}
// Continued on Next Slide
Eduardo Nicola F. Zagari
39
Comunicação e Sincronização
41. Problema da Solução de Peterson e da Instrução TSL: o
processo que não consegue acesso à R.C. permanece em
“espera ocupada” (busy waiting):
gasta tempo de processador inutilmente
pode provocar o Problema da Inversão de Prioridade:
Dois processos A (alta prioridade) e B (baixa prioridade)
» B entra na Região Crítica;
» A vai para estado de pronto;
» A passa para estado de execução;
» A fica testando indefinidamente.
Chamadas de sistema: SLEEP e WAKEUP
Eduardo Nicola F. Zagari
41
Comunicação e Sincronização
42. Dois processos compartilham um buffer de tamanho fixo. Um
dos processos, o produtor, coloca informação no buffer, e o
outro, o consumidor, retira informação do buffer.
Se o buffer estiver cheio, o produtor dorme e é acordado quando
o consumidor remover um item
Se o buffer estiver vazio, o consumidor dorme até que seja
produzido e armazenado algum item
Eduardo Nicola F. Zagari
42
Comunicação e Sincronização
43. #define N 100 /* no máximo de ítens */
int count = 0; /* no de ítens no buffer */
Produtor Consumidor
void producer (void) { void consumer(void) {
int item; int item;
while (TRUE) { while (TRUE) {
produce_item(&item); if (count==0) sleep();
if (count==N) sleep(); remove_item(&item);
enter_item(item); count -= 1;
count += 1; if (count==N-1)
if (count==1) wakeup(producer);
wakeup(consumer); consume_item(item);
} }
} }
Eduardo Nicola F. Zagari
43
Comunicação e Sincronização
44. Problema:
Se o buffer estiver vazio e o consumidor sofrer preempção antes de
dormir o produtor produz e envia um wakeup para o
consumidor que ainda não está dormindo o sinal se perde e o
consumidor dormirá para sempre...
Eduardo Nicola F. Zagari
44
Comunicação e Sincronização
45. Mecanismo de sincronização que não requer espera ocupada
Dijkstra propôs usar uma variável inteira para contar o no de
WAKEUPs para uso futuro
Menos complicado
Esta variável, denominada semáforo, pode ter valor 0 (nenhum
WAKEUP pendente) ou um valor inteiro positivo
Apenas duas operações são definidas sobre estas variáveis: as
operações P (Down) e V (Up)
Eduardo Nicola F. Zagari
45
Comunicação e Sincronização
46. P(s) (Down) - checa o valor do semáforo. Se o valor é maior que 0 (zero),
decrementa e continua. Se for igual a 0, o processo é posto para dormir
Ação atômica: é garantido que, uma vez iniciada a operação, nenhum
outro processo tem acesso ao semáforo (essencial para resolver
problemas de sincronização e evitar condições de corrida)
P(s) equivale a:
Se s > 0 então
s := s - 1
senão
bloqueia o processo até s > 0 (= wait(s))
V(s) (Up) - se um ou mais processos estão dormindo no semáforo, um deles é
escolhido aleatoriamente pelo SO e continua sua operação Down (o valor
zero continua). Se não há ninguém “dormindo” no semáforo, incrementa o
valor dele
Operação também é indivisível
V(s) equivale a:
Verifica se existe uma lista com processos bloqueados por causa
de s, se existe
escolhe um e o “acorda”, deixando-o pronto para seguir sua
execução de P(s) (= signal(s))
senão
s := s + 1
Eduardo Nicola F. Zagari
46
Comunicação e Sincronização
47. Semáforo Contador – valor inteiro positivo pode variar sem
limites
Semáforo Binário – valor inteiro só pode variar entre 0 e 1;
Também conhecido como mutex locks
Para fornecer exclusão mútua:
Semaphore S; // initialized to 1
P(S);
criticalSection();
V(S);
Eduardo Nicola F. Zagari
47
Comunicação e Sincronização
48. #define N 100 /* no máximo de ítens */
typedef int semaphore;
semaphore mutex = 1; /* controla acesso à RC */
semaphore empty = N; /* conta slots vazios */
semaphore full = 0; /* conta slots ocupados */
void producer (void) { void consumer(void) {
int item; int item;
while (TRUE) { while (TRUE) {
produce_item(&item); P(&full);
P(&empty); P(&mutex);
P(&mutex); remove_item(&item);
enter_item(item); V(&mutex);
V(&mutex); V(&empty);
V(&full); consume_item(item);
} }
} }
Eduardo Nicola F. Zagari
48
Comunicação e Sincronização
49. public class Worker implements Runnable
{
private Semaphore sem;
private String name;
public Worker(Semaphore sem, String name) {
this.sem = sem;
this.name = name;
}
public void run() {
while (true) {
sem.acquire();
MutualExclusionUtilities.criticalSection(name);
sem.release();
MutualExclusionUtilities.nonCriticalSection(name);
}
}
}
Eduardo Nicola F. Zagari
49
Comunicação e Sincronização
50. public class SemaphoreFactory
{
public static void main(String args[]) {
Semaphore sem = new Semaphore(1);
Thread[] bees = new Thread[5];
for (int i = 0; i < 5; i++)
bees[i] = new Thread(new Worker
(sem, "Worker " + (new Integer(i)).toString() ));
for (int i = 0; i < 5; i++)
bees[i].start();
}
}
Eduardo Nicola F. Zagari
50
Comunicação e Sincronização
51. Deadlock – dois ou mais processos ficam esperando
indefinidamente por um evento que pode ser causado apenas
por um dos processos bloqueados
Sejam S e Q dois semáforos inicializados com 1
P0
P1
P(S);
P(Q);
P(Q);
P(S);
.
.
.
.
.
.
V(S);
V(Q);
V(Q);
V(S);
Starvation – bloqueio indefinido. Um processo pode nunca ser
removido da fila de semáforos na qual ele está bloqueado.
Eduardo Nicola F. Zagari
51
Comunicação e Sincronização
52. Problema do Buffer Limitado
Problema dos Leitores e Escritores
Problema do Jantar dos Filósofos
Eduardo Nicola F. Zagari
52
Comunicação e Sincronização
53. public class BoundedBuffer implements Buffer
{
private static final int BUFFER SIZE = 5;
private Object[] buffer;
private int in, out;
private Semaphore mutex;
private Semaphore empty;
private Semaphore full;
// Continued on next Slide
Eduardo Nicola F. Zagari
53
Comunicação e Sincronização
54. public BoundedBuffer() {
// buffer is initially empty
in = 0;
out = 0;
buffer = new Object[BUFFER SIZE];
mutex = new Semaphore(1);
empty = new Semaphore(BUFFER SIZE);
full = new Semaphore(0);
}
public void insert(Object item) { /* next slides */ }
public Object remove() { /* next slides */ }
}
Eduardo Nicola F. Zagari
54
Comunicação e Sincronização
55. public void insert(Object item) {
empty.acquire();
mutex.acquire();
// add an item to the buffer
buffer[in] = item;
in = (in + 1) % BUFFER SIZE;
mutex.release();
full.release();
}
Eduardo Nicola F. Zagari
55
Comunicação e Sincronização
56. public Object remove() {
full.acquire();
mutex.acquire();
// remove an item from the buffer
Object item = buffer[out];
out = (out + 1) % BUFFER SIZE;
mutex.release();
empty.release();
return item;
}
Eduardo Nicola F. Zagari
56
Comunicação e Sincronização
57. import java.util.Date;
public class Producer implements Runnable
{
private Buffer buffer;
public Producer(Buffer buffer) {
this.buffer = buffer;
}
public void run() {
Date message;
while (true) {
// nap for awhile
SleepUtilities.nap();
// produce an item & enter it into the buffer
message = new Date();
buffer.insert(message);
}
}
}
Eduardo Nicola F. Zagari
57
Comunicação e Sincronização
58. import java.util.Date;
public class Consumer implements Runnable
{
private Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
public void run() {
Date message;
while (true) {
// nap for awhile
SleepUtilities.nap();
// consume an item from the buffer
message = (Date)buffer.remove();
}
}
}
Eduardo Nicola F. Zagari
58
Comunicação e Sincronização
59. public class Factory
{
public static void main(String args[]) {
Buffer buffer = new BoundedBuffer();
// now create the producer and consumer threads
Thread producer = new Thread(new Producer(buffer));
Thread consumer = new Thread(new Consumer(buffer));
producer.start();
consumer.start();
}
}
Eduardo Nicola F. Zagari
59
Comunicação e Sincronização
60. Acesso a uma base de dados
É aceitável que haja mais de um processo lendo a base de
dados ao mesmo tempo, mas...
... se um processo estiver escrevendo na base, nenhum outro
processo, nem mesmo os leitores, podem ter acesso a ela,
enquanto o escritor não tiver terminado
Eduardo Nicola F. Zagari
60
Comunicação e Sincronização
61. void reader (void) {
while (TRUE) {
Program Solução; P(&mutex);
typedef int semaphore;
rc++;
semaphore mutex = 1;
semaphore db = 1; if (rc == 1)
int rc = 0; P(&db);
void writer (void) { V(&mutex);
while (TRUE) { read_data_base();
think_up_data(); P(&mutex);
P(&db); rc--;
write_data_base(); if (rc == 0)
V(&db); V(&db);
} V(&mutex);
} use_data_read();
}
Eduardo Nicola F. Zagari
61
} Comunicação e Sincronização
62. public class Reader implements Runnable
{
private RWLock db;
public Reader(RWLock db) {
this.db = db;
}
public void run() {
while (true) { // nap for awhile
db.acquireReadLock();
// you now have access to read from the database
// read from the database
db.releaseReadLock();
}
}
}
Eduardo Nicola F. Zagari
62
Comunicação e Sincronização
63. public class Writer implements Runnable
{
private RWLock db;
public Writer(RWLock db) {
this.db = db;
}
public void run() {
while (true) {
db.acquireWriteLock();
// you have access to write to the database
// write to the database
db.releaseWriteLock();
}
}
}
Eduardo Nicola F. Zagari
63
Comunicação e Sincronização
64. public interface RWLock
{
public abstract void acquireReadLock();
public abstract void acquireWriteLock();
public abstract void releaseReadLock();
public abstract void releaseWriteLock();
}
Eduardo Nicola F. Zagari
64
Comunicação e Sincronização
65. public class Database implements RWLock
{
private int readerCount;
private Semaphore mutex;
private Semaphore db;
public Database() {
readerCount = 0;
mutex = new Semaphore(1);
db = new Semaphore(1);
}
public int acquireReadLock() { /* next slides */ }
public int releaseReadLock() {/* next slides */ }
public void acquireWriteLock() {/* next slides */ }
public void releaseWriteLock() {/* next slides */ }
}
Eduardo Nicola F. Zagari
65
Comunicação e Sincronização
66. public void acquireReadLock() {
mutex.acquire();
++readerCount;
// if I am the first reader tell all others
// that the database is being read
if (readerCount == 1)
db.acquire();
mutex.release();
}
public void releaseReadLock() {
mutex.acquire();
--readerCount;
// if I am the last reader tell all others
// that the database is no longer being read
if (readerCount == 0)
db.release();
mutex.release();
}
Eduardo Nicola F. Zagari
66
Comunicação e Sincronização
67. public void acquireWriteLock() {
db.acquire();
}
public void releaseWriteLock() {
db.release();
}
Eduardo Nicola F. Zagari
67
Comunicação e Sincronização
69. Cinco filósofos passam a vida meditando em suas salas e,
quando têm fome, vão ao refeitório, comem e voltam a meditar
Para um filósofo comer é necessário conseguir os dois
chopsticks (pauzinhos chineses) que, originalmente, estão à
esquerda e à direita do lugar onde se sentou
Um filósofo pode sentar em qualquer dos cinco lugares
O estoque de comida é interminável
Deve-se garantir:
exclusão mútua
não ocorrência de deadlock
não ocorrência de adiamento indefinido (starvation)
não existência de prioridade para algum filósofo
possibilidade de velocidades diferenciadas
Eduardo Nicola F. Zagari
69
Comunicação e Sincronização
70. Program Tentativa_Um;
var pauzinho: array[1..5] of semaforo;
i: integer;
Procedure filosofo(i:integer) Begin
begin for i:= 1 to 5 do
while TRUE do pauzinho[i] := 1;
begin Cobegin
Pensando; filosofo(1);
P(pauzinho[i]); filosofo(2);
P(pauzinho[(i+1)mod N]); filosofo(3);
Comendo; filosofo(4);
V(pauzinho[i]); filosofo(5);
V(pauzinho[(i+1)mod N]); Coend;
end; End.
end;
Eduardo Nicola F. Zagari
70
Comunicação e Sincronização
71. Não funciona se os filósofos resolverem comer ao mesmo
tempo e cada um pegar o pauzinho esquerdo
não haverá pauzinho direito (deadlock)
Pode-se modificar o programa de tal forma que, após pegar
pauzinho esquerdo, verifica-se se o pauzinho direto está livre
se não, põe-se de volta o pauzinho
suponha que todos peguem e testem juntos
adiamento indefinido (starvation)
Se cada filósofo aguardar um tempo randômico
tempo de resposta variável (além de
ainda ser susceptível à falha de inanição)
Eduardo Nicola F. Zagari
71
Comunicação e Sincronização
72. Program Tentativa_Dois;
var lugar: semaforo;
Procedure filosofo; Begin
begin lugar := 1;
while TRUE do Cobegin
begin filosofo;
Pensando; filosofo;
P(lugar); filosofo;
Comendo; filosofo;
V(lugar); filosofo;
end; Coend;
end; End.
Eduardo Nicola F. Zagari
72
Comunicação e Sincronização
73. Tentativa Dois: proteção através de semáforo binário antes de
começar a comer
demais filósofos ficam esperando
o primeiro liberar o pauzinho
Na prática, não funciona a cada instante, apenas um filósofo
senta-se à mesa para comer
sub-utilização dos recursos do sistema
Eduardo Nicola F. Zagari
73
Comunicação e Sincronização
74. Program Tentativa_Três;
var pauzinho: array[1..5] of semaforo;
lugar: semaforo, i: integer;
Procedure filosofo(i:integer) Begin
begin lugar := 4;
while TRUE do begin for i:= 1 to 5 do
Pensando; pauzinho[i] := 1;
P(lugar); Cobegin
P(pauzinho[i]); filosofo(1);
P(pauzinho[(i+1)mod N]); filosofo(2);
Comendo; filosofo(3);
V(pauzinho[i]); filosofo(4);
V(pauzinho[(i+1)mod N]); filosofo(5);
V(lugar); Coend;
end; End.
end;
Eduardo Nicola F. Zagari
74
Comunicação e Sincronização
75. Tentativa Três: no máximo quatro filósofos sentam-se à mesa ao
mesmo tempo
Para 5 garfos disponíveis, devemos permitir que dois filósofos
comam ao mesmo tempo
Não funciona apenas um irá comer por vez (posse de dois
pauzinhos)
Solução não controlar os recursos, mas os estados dos
filósofos...
Eduardo Nicola F. Zagari
75
Comunicação e Sincronização
76. Program Solução;
Cons N = 5; Pensando = 0; Com_fome = 1; Comendo = 2;
Type situacao = [Pensando, Com_fome, Comendo];
var pode_sentar: array[1..5] of semaforo;
estado: array[1..5] of situacao;
mutex: semaforo; Begin
i: integer; mutex := 1;
Procedure filosofo(i:integer)
for i:= 1 to 5 do begin
estado[i] := Pensando;
begin
pode_sentar[i] := 0;
while TRUE do begin
end;
Pensando;
Cobegin
Pega_pauzinho(i);
filosofo(1); filosofo(2);
Comendo;
filosofo(3); filosofo(4);
Coloca_pauzinho(i);
filosofo(5);
end;
Coend;
end;
End.
Eduardo Nicola F. Zagari
76
Comunicação e Sincronização
77. Procedure pega_pauzinho(i:integer);
begin
Procedure
P(mutex); coloca_pauzinho(i:integer);
estado[i] := Com_fome; begin
testa(i); P(mutex);
V(mutex); estado[i] := Pensando;
P(pode_sentar[i]); testa((i-1) mod N);
end; testa((i+1) mod N);
Procedure testa(i:integer); V(mutex);
begin end;
if (estado[i]= Com_fome and estado[(i-1)mod N] <> Comendo and
estado[(i+1) mod N] <> Comendo) then begin
estado[i] := Comendo;
V(pode_sentar[i]);
end;
end.
Eduardo Nicola F. Zagari
77
Comunicação e Sincronização
78. Dados compartilhados
Semaphore chopStick[] = new Semaphore[5];
Filósofo i:
while (true) {
// get left chopstick
chopStick[i].acquire();
// get right chopstick
chopStick[(i + 1) % 5].acquire();
eating();
// return left chopstick
chopStick[i].release();
// return right chopstick
chopStick[(i + 1) % 5].release();
thinking();
}
Eduardo Nicola F. Zagari
78
Comunicação e Sincronização
79. Considere que, no projeto da solução do produtor-consumidor
com semáforos, os 2 “downs” tenham sido trocados
Se o buffer estiver cheio deadlock
Monitor = coleção de variáveis, estruturas e procedimentos que
são agrupados em um módulo ou pacote (tipo abstrato de
dados com operações bem definidas para atuarem sobre sua
estrutura de dados)
Um monitor é uma abstração de alto nível que provê exclusão
mútua
Os procedimentos de um monitor só executam alguma tarefa
quando chamados por processos (são passivos) e operam sobre
variáveis comuns que constituem sua estrutura de dados
Eduardo Nicola F. Zagari
79
Comunicação e Sincronização
80. Os processos podem chamar procedimentos dos monitores,
mas não têm acesso à estrutura interna dos mesmos
Somente um processo pode estar ativo em um monitor em cada
instante, sendo assim, definem regiões críticas criando exclusão
mútua para a estrutura de dados do monitor
Quando um processo chama um procedimento num monitor,
verifica-se se outro processo está ativo no monitor
Sim: processo suspenso
Não: processo pode entrar na Região Crítica
A sincronização entre processos concorrentes se faz através de
operações WAIT e SIGNAL sobre variáveis do tipo evento,
definidas dentro do monitor
Uma thread (ou procedimento) que invoca x.wait é suspensa até
que outra(o) invoque x.signal
Implementação: precisa de suporte da linguagem
Eduardo Nicola F. Zagari
80
Comunicação e Sincronização
81. Somente uma thread (ou procedimento) pode estar ativo dentro
do monitor por vez
monitor monitor-name
{
// variable declarations
public entry p1(…) {
…
}
public entry p2(…) {
…
}
}
Eduardo Nicola F. Zagari
81
Comunicação e Sincronização
83. procedure producer; procedure consumer;
begin begin
while true do while true do
begin begin
produce_item; ProduceConsumer.remove;
ProduceConsumer.enter; consume_item;
end; end;
end; end;
Eduardo Nicola F. Zagari
83
Comunicação e Sincronização
84. monitor ProducerConsumer
condition full, empty;
integer count;
procedure enter;
begin
if count = N then wait(full);
enter_item;
count := count + 1;
if count = 1 then signal(empty);
end;
procedure remove;
begin
if count = 0 then wait(empty);
remove_item;
count := count - 1;
if count = N - 1 then signal(full);
end;
count := 0;
end monitor;
Eduardo Nicola F. Zagari
84
Comunicação e Sincronização
85. monitor DiningPhilosophers {
int[] state = new int[5];
static final int THINKING = 0;
static final int HUNGRY = 1;
static final int EATING = 2;
condition[] self = new condition[5];
public diningPhilosophers {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
public entry pickUp(int i) {
state[i] = HUNGRY;
test(i);
if (state[i] != EATING)
self[i].wait;
}
// Continued on Next Slide
Eduardo Nicola F. Zagari
85
Comunicação e Sincronização
86. public entry putDown(int i) {
state[i] = THINKING;
// test left and right neighbors
test((i + 4) % 5);
test((i + 1) % 5);
}
private test(int i) {
if ( (state[(i + 4) % 5] != EATING) &&
(state[i] == HUNGRY) &&
(state[(i + 1) % 5] != EATING) ) {
state[i] = EATING;
self[i].signal;
}
}
}
Eduardo Nicola F. Zagari
86
Comunicação e Sincronização
87. Os mecanismos anteriores permitem comunicação indireta
O mecanismo de troca de mensagens entre um processo gerente,
destinado a disciplinar a utilização de determinado recurso, e os
processos concorrentes pode ser usado para se obter exclusão
mútua
Duas primitivas -não há compartilhamento de memória
SEND(destino, msg)
destino: processo destinatário (ex.: PID)
msg: mensagem que se quer enviar
RECEIVE(fonte, &msg)
fonte: processo que enviou
msg: mensagem retornada, se existir
– Caso não exista mensagem, o processo pode ser bloqueado
até a existência de uma
Eduardo Nicola F. Zagari
87
Comunicação e Sincronização
88. Identificação dos processos: processo@máquina
Nomes de máquinas iguais domínio (proc@máq.domínio)
O armazenamento e a sincronização são feitos pelas primitivas,
ou seja, pelo SO, que é o responsável
A mensagem pode se perder na rede:
mensagem de reconhecimento (acknowledgement)
se ACK não chegar, retransmite
e se a msg chega, mas o ACK se perde?
fonte envia msg novamente no de identificação de msg
Confiabilidade autenticação
Se transmissor e receptor estão na mesma máquina, o
rendimento cai pelo excesso de cópias de dados
Eduardo Nicola F. Zagari
88
Comunicação e Sincronização
89. #include “prototypes.h”
#define N 100
#define MSIZE 4 void consumer(void) {
typedef int message[4]; int item, i;
void producer(void) { message m;
int item; for(i=0; i<N; i++)
message m; send(prod, &m);
while (TRUE) { while (TRUE) {
produce_item(&item); receive(prod, &m);
receive(cons, &m); extract_msg(&m,item);
build_msg(&m, item); send(prod, &m);
send(cons, &m); consume_item(item);
} }
} }
Eduardo Nicola F. Zagari
89
Comunicação e Sincronização