Implementação de Sincronização Protocolos que usam busy waiting: Difíceis de projetar, compreender e provar corretos Complexos Variáveis usadas para sincronizar são idênticas às outras variáveis do programa ineficientes Solução usar ferramentas especiais para ajudar a projetar protocolos de sincronização corretos, e bloqueiam processos em espera. Semáforos Foi uma das primeiras “ferramentas” Uma das mais importantes Torna fácil proteger regiões críticas Pode ser usado de forma disciplinada para implementar sincronização de condição Podem ser implementados com busy-waiting ou interagir com o escalonador de processos para obter sincronização sem busy-waiting Semáforo Tipo abstrato de dados, com duas operações: P(S) Também chamada de wait Atrasa um processo até que um evento ocorra. V(S) Também chamado de signal Sinaliza a ocorrencia de um evento Invariante do Semáforo Para um semáforo s, seja nP o número de operações P completadas e nV o número de operações V completadas. Se init é o valor inicial de s, então, em todos os estados visíveis, nP nV + init Execução de operações P esperam até que um número adequado de operações V tenha ocorrido Representação de um Semáforo Número não negativo s = init + nV – nP Invariante: s 0 Representação de um Semáforo P (s) <await (s > 0) -> s := s – 1> V (s) <s := s + 1> Tipos de Semáforos Semáforo genérico assume valores não negativos Semáforo binário assume apenas valores 0 ou 1 Região Crítica sem_t S = 1; void Pi { while (TRUE) { Non_Cr_S_i; P(S); Critical_S_i; V(S); } } A propriedade de exclusão mútua é satisfeita #CS é o número de processos em sua região crítica provar que #CS + S = 1 #CS = nP(S) - nV(S) S = 1 + nV(S) - nP(S) S = 1 - #CS #CS + S = 1 como S >= 0, então #CS <= 1 O programa não entra em deadlock os processos teriam que estar suspensos em P(S) teríamos S = 0 e #CS = 0 mas já mostramos que #CS + S = 1 contradição não há starvation Se P1 está suspenso, o semáforo deve ter 0 Pelo invariante do semáforo, P2 está na região crítica Quando P2 sai da região crítica, executa V(S), que acorda um processo (P1) Definições de semáforos Blocked-set semaphore um sinal acorda um dos processos suspensos Blocked-queue semaphore os processos suspensos são mantidos em uma fila FIFO e são acordados na mesma ordem em que foram suspensos Definições de semáforos Busy-wait semaphore processos ficam testando o valor de S em um loop de busy-wait. O teste é atômico. Definições de semáforos Strongly-fair semaphore se o semáforo é sinalizado infinatamente frequente, em algum momento todos os processos esperando serão acordados. Weakly-fair semaphore se o semáforo é mantido com um valor maior que zero, em algum momento todos os processos esperando serão atendidos. Provas em um semáforo de busy-wait, pode ocorrer starvation P1 executa P(S) e entra na região crítica P2 encontra S = 0 e fica no loop P1 executa todo um ciclo, e reentra na região crítica P2 encontra S = 0 e fica no loop Provas em um semáforo blocked-queue, é impossível ocorrer starvation suponha P1 bloqueado em S então existem no máximo N - 1 processos na frente de P1 na fila para S Provas em um semáforo blocked-set, é possível ocorrer starvation para N >= 3 como não há garantia de qual processo será acordado, se houverem pelo menos 2 processos bloqueados, um deles pode ser sempre o escolhido. Barreiras sem_t barrier1 = 0; sem_t barrier2 = 0; void P2 { void P1 { while (TRUE) { while (TRUE) { ...; ...; V(barrier2); V(barrier1); P(barrier1); P(barrier2); } } } } Problema do ProdutorConsumidor Problema de comunicação Produtores geram dados com o procedimento Produce, que devem ser enviados aos consumidores Consumidores ao receberem um dado fazem alguma computação, com o procedimento Consume Buffer de tamanho 1 int B; sem_t empty = 1; sem_t full = 0; void Producer { int m; while (TRUE) { Produce(m); P(empty); B = m; V(full); } } void Consumer { int m; while (TRUE) { P(full); m = B; V(empty); Consume(m); } } Buffer limitado (circular) int B[n]; int front = 0; int rear = 0; sem_t empty = n; sem_t full = 0; void Consumer { void Producer { int m; int m; while (TRUE) { while (TRUE) { P(full); Produce(m); m = B[front]; P(empty); B[rear] = m; front = (front + 1) % n rear = (rear + 1) % n V(empty); V(full); Consume(m); } } } } Buffer limitado – com vários produtores e consumidores int B[n]; int front = 0; int rear = 0; sem_t empty = n; sem_t full = 0; sem_t mutexD = 0; sem_t mutexF = 0; void Consumer { void Producer { int m; int m; while (TRUE) { while (TRUE) { P(full); P(mutexF); Produce(m); m = B[front]; P(empty); P(mutexD); front = (front + 1) % n B[rear] = m; V(mutexF); V(empty); rear = (rear + 1) % n Consume(m); V(mutexD); V(full); } } } } Exclusão mútua seletiva Jantar dos filósofos Processos competem por acesso a conjuntos sobrepostos de variáveis compartilhadas Leitores e Escritores Combinação de acesso concorrente e exclusivo a variáveis compartilhadas Jantar dos filósofos Cinco filósofos ao redor de uma mesa redonda Cada filósofo alterna entre comer e pensar O spaghetti está no centro da mesa Filósofos precisam de dois garfos para se alimentar Existem 5 garfos, um entre cada par de filósofos Evitar deadlock, e outras propriedades. Jantar dos Filósofos Fil. 1 Fil. 2 Fil. 0 Fil. 3 Fil. 4 Solução sem_t fork[4] = 1; void Philosopher[i] { /* filósofo 1 a 4 */ while (TRUE) { Think; P(fork[i]); P(fork[(i+1) % 5]); Eat; V (fork[i]); V (fork[(i+1) % 5]); } } Solução 2 sem_t fork[4] = 1; void Philosopher[i] { /* filósofo 0 a 3 */ while (TRUE) { Think; P(fork[i]); P(fork[(i+1) % 5]); Eat; V (fork[i]); V (fork[(i+1) % 5]); } } Solução 2 (cont.) sem_t fork[4] = 1; void Philosopher[4] { /* filósofo 4 */ while (TRUE) { Think; P(fork[0]); P(fork[4]); Eat; V (fork[4]); V (fork[0]); } } Leitores e Escritores Dois tipos de processos: leitores e escritores, compartilham o acesso a uma base de dados. Leitores lêem valores da base de dados. Escritores escrevem na base de dados. A Base de dados está inicialmente em um estado consistente. Cada transação transforma a base de dados de um estado consistente em outro. Leitores e Escritores Para evitar interferência o escritor deve ter acesso exclusivo à base de dados Se nenhum escritor estiver acessando a base de dados, vários leitores podem ler concorrentemente a base de dados Exclusão mútua seletiva Solução Variáveis para controlar leitores e escritores reading, writing[n] Esboço da Solução int reading = 0; int nr = 0; int writing[n] = 0; void Reader[i] { while (TRUE) { <nr++; if (nr == 1) {reading++};> read the database <nr--; if (nr == 0) {reading--};> } } void Writer[i] { while (TRUE) { <writing[i]++;> write the database <writing[i]--;> } } Solução de grossa granularidade int reading = 0; int nr = 0; int writing[n] = 0; void Reader[i] { while (TRUE) { <nr++; if (nr == 1) {await (SUM0) -> reading++};> read the database <nr--; if (nr == 0) {reading--};> } } void Writer[i] { while (TRUE) { <await (SUM 0) -> writing[i]++;> write the database <writing[i]--;> } } Solução de grossa granularidade int reading = 0; int nr = 0; int writing[n] = 0; sem_t mutexR = 1; void Reader[i] { while (TRUE) { P(mutexR); nr++; if (nr == 1) {<await (SUM0) -> reading++>}; V(mutexR); read the database P(mutexR); nr--; if (nr == 0) {< reading-- >}; V(mutexR); } } Solução int nr = 0; sem_t mutexR = 1; sem_t rw = 1; void Reader[i] { while (TRUE) { P(mutexR); nr++; if (nr == 1) {P(rw)}; V(mutexR); read the database P(mutexR); nr--; if (nr == 0) {V(rw)}; V(mutexR); } } Solução void Writer[i] { while (TRUE) { P(rw); write the database V(rw); } } Solução Prioridade para os escritores Outra solução: controlar quais processos estão em espera, e escolher outro tipo de prioridade contar número de leitores e escritores na região crítica (nr, nw), número de leitores e escritores esperando (dr, dw). Solução 2 int nr = 0; nw = 0; sem_t e = 1; sem_t r = 0; sem_t w = 0; int dr = 0; int dw = 0; Solução 2 void Reader[i] { while (TRUE) { P(e); if (nw > 0) {dr++; V(e); P(r)}; nr++; if (dr > 0) {dr--; V(r)} else {V(e)}; read the database P(e); nr--; if (nr == 0 && dw > 0) {dw--;V(w)} else {V(e)}; } } Solução 2 void Writer[i] { while (TRUE) { P(e); if (nr > 0 || nw > 0) {dw++; V(e); P(w)}; nw++; V(e); read the database P(e); nw--; if (dr > 0) {dr--;V(r)} else {if (dw > 0) {dw--;V(w)} else {V(e)}}; } }