
Fonte da imagem: Priyank Verma
#include <stdio.h>
#include <pthread.h>
int contador = 0;
void* incrementa_contador(void* thread_id) {
int tid = (int)(long)thread_id; // Cast para obter o ID da thread
for (int i = 0; i < 10000; i++) {
contador++; // Acesso direto à variável global sem proteção
}
printf("Thread %d: Contador final = %d\n", tid, contador);
pthread_exit(0);
}
int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, incrementa_contador, (void*)1); // Cria thread1
pthread_create(&thread2, NULL, incrementa_contador, (void*)2); // Cria thread2
pthread_join(thread1, NULL); // Espera a thread1 terminar
pthread_join(thread2, NULL); // Espera a thread2 terminar
printf("Valor final do contador: %d\n", contador); // O valor final será imprevisível
return 0;
}
Bloco de código que acessa um recurso compartilhado.
entrar na região crítica
acessar ou modificar recurso compartilhado
sair da região crítica
Exemplos de recursos compartilhados:
Exclusão mútua significa garantir que apenas um processo por vez acesse uma seção crítica.
Objetivos:
Se Processo A está na seção crítica,
Processo B deve esperar.
Todo algoritmo de exclusão mútua deve satisfazer 3 propriedades:
| # | Propriedade | Descrição |
|---|---|---|
| 1 | Exclusão Mútua | Apenas 1 processo na seção crítica por vez. Sempre. |
| 2 | Progresso | Se nenhum está na seção crítica, quem quer entrar deve conseguir. |
| 3 | Espera Limitada | Nenhum processo espera para sempre. Starvation proibido. |
Processos rodam em servidores diferentes, ligados por rede.
Precisamos de um protocolo explícito de coordenação.
Exemplo: Servidor A e Servidor B tentam atualizar o mesmo registro no banco de dados ao mesmo tempo. Qual atualização vale? Quem chegou primeiro?
Cada processo mantém um relógios lógicos usado em algoritmos de sincronização de relógio baseados na relação happens-before definida por Leslie Lamport.

Na imagem: (a) Relógios de Lamport são sequências de números ou contadores. (b) O Algoritmo de Lamport corrige os relógios. Fonte: StackOverflow.
Quando um processo envia uma mensagem, ele inclui o valor do seu contador (relógio) na mensagem.

Diagrama de relógios de Lamport. Fonte da imagem: Wikipedia.
Desafio principal: Em um sistema distribuído, como decidir quem pode acessar um recurso compartilhado?
Exemplo:
Nó A quer acessar um arquivo distribuído.
Nó B quer acessar o mesmo arquivo.
Nó C também quer acessar o mesmo arquivo.
Sem um controle adequado, os três podem tentar modificar o recurso ao mesmo tempo.
A exclusão mútua distribuída busca coordenar processos em máquinas diferentes.
O problema é garantir que:
Apenas um processo distribuído acesse o recurso crítico por vez.
Isso deve ocorrer mesmo sem:
2(N-1) mensagens por entrada
Fonte da Imagem: quest10.com.
Um algoritmo descentralizado não depende de um único coordenador.
Ideia geral:
Processo P1 quer entrar na região crítica.
P1 -> P2: pedido de acesso
P1 -> P3: pedido de acesso
P1 -> P4: pedido de acesso
P2 -> P1: permitido
P3 -> P1: permitido
P4 -> P1: permitido
P1 entra na região crítica.
A entrada só ocorre após receber as autorizações necessárias.
Passo a passo:
REQUEST(ts, P) para todos os nósOK — ou guarda na fila se tiver prioridadeOK para os processos na filaRegra de desempate: menor timestamp ganha. Em caso de empate de timestamp, menor ID de processo vence.
Ao querer entrar na seção crítica:
ts = meu_timestamp + 1
enviar REQUEST(ts, eu) para TODOS
aguardar OK de TODOS os outros nós
Ao receber REQUEST(ts_deles, deles):
SE estou na seção crítica:
→ adicionar à fila (não responder agora)
SENÃO SE quero entrar E tenho prioridade:
→ (meu_ts < ts_deles)
OU (empate: meu_id < id_deles)
→ adicionar à fila (não responder agora)
SENÃO:
→ responder OK imediatamente
Ao sair da seção crítica:
enviar OK para todos os processos na fila
Visão Geral
Funcionamento:
O token funciona como uma “chave” que autoriza o uso do recurso compartilhado.
funcao Processo_i(Recebeu_Token):
// 1. Entra na Seção Crítica
Executar_Tarefa();
// 2. Passa o Token adiante
Enviar_Token(Processo_(i+1));
+------+ +------+
| P1 | -----> | P2 |
+------+ +------+
^ |
| v
+------+ +------+
| P4 | <----- | P3 |
+------+ +------+
O token circula em uma ordem definida.Imagine uma linha de montagem onde o "sinal verde" (token) só é passado quando a peça está pronta.
Vantagens:
Ideia importante: não há disputa simultânea se só existe um token válido.
Desvantagens:
Ao comparar algoritmos de exclusão mútua distribuída, analisamos:
Considere três processos distribuídos: P1, P2 e P3.
Todos desejam acessar o mesmo arquivo compartilhado.
Perguntas:
Analise a situação:
P1 está com o token.
P2 precisa acessar a região crítica.
P3 também precisa acessar a região crítica.
P1 falha antes de repassar o token.
Perguntas:
Pergunta: O que acontece se o nó que tem o token falhar durante a seção crítica? Como o sistema detecta essa falha e regenera o token com segurança?
Dúvidas e Discussão
Em um sistema distribuído real, o que é mais difícil de lidar?
Explique a principal diferença arquitetural e de resiliência entre usar um Lock Manager Centralizado versus um sistema baseado em Passagem de Token. Em qual cenário você preferiria cada abordagem?
Descreva o passo a passo (em 4 etapas) que ocorre desde o momento em que o Processo A recebe o Token até o momento em que ele passa o Token para o Processo B, garantindo que nenhum outro processo possa interferir nesse intervalo.
Considere um sistema distribuído usando Passagem de Token. Se o Processo C falhar depois de ter executado a Seção Crítica, mas antes de passar o Token para o Processo D, qual é o estado do sistema? Como esse problema pode ser mitigado em teoria (sem detalhar algoritmos complexos)?
Em um ambiente onde os processos são muito numerosos e as operações na Seção Crítica são extremamente rápidas (milissegundos), qual dos mecanismos de exclusão mútua (Centralizado ou Token) tende a gerar mais overhead (custo extra de comunicação)? Justifique sua resposta.
- Em sistemas locais: semáforos e mutexes. - Em sistemas distribuídos: **algoritmos de coordenação**
# Tratando conflitos E se dois processos pedirem acesso **ao mesmo tempo**? ```text P1 quer entrar na região crítica. P2 também quer entrar na região crítica. ``` O algoritmo precisa de uma **regra de desempate**, por exemplo: - Ordem dos pedidos. - Timestamp lógico. - Identificador do processo. Assim, todos os processos chegam à mesma decisão sobre quem tem prioridade. ---