Modelagem e implementação de programas
concorrentes
Aula 5
DCC-UFMG
Bibliograa
A de sempre +
The Mutual Exclusion Problem Part I: A Theory of Interprocess Communication L. Lamport
Interferencia
Interferência: instruções executadas por um processo invalidam asserções em outro. Aparece por causa das variáveis compartilhadas A ação a não interfere com a asserção C se {C ∧ pre(a)} a {C} é sempre Verdadeiro (C é invariante respeito a execução de a) Exemplo:
x = 1; // y = 2;
z = x+y; z = x-y;
Interferencia
1. variables disjuntas processos independentes Sobreposição (interação de processos):
1. asserções enfraquecidas armamos menos do que poderiamos na execução isolada
2. invariantes globais predicados que são Verdadeiros em todos os estados visíveis
3. sincronização esconde estados (exclusão mutua <S>) ou demora a execução (sincronização por condição <await B S>)
Seção crítica
Seção crítica
Seção crítica: seqüência de ações envolvida no acesso a variáveis compartilhadas
Problema da seção crítica: N processos executam em um loop innito uma sequencia de instruções dividida em 2 subsequencias: a seção critica e a seção nao-critica.
<Si> ≡ CSEnter Si CSExit.
Os processos não podem parar dentro da CS (em algum momento saem)
A solução ao problema da seção crítica deve satisfazer 3 regras:
I Exclusão mutua
I Progresso (deadlock-freedom) I Espera limitada
Seção crítica
I Exclusão mutua: Se o processo P1 está executando na sua
seção crítica, então nenhum outro processo pode ser
executado nessa seção crítica (não pode ter dois processos na seção crítica ao mesmo tempo);
Seção crítica
I Progresso (deadlock-freedom?): Se um processo está tentando
entrar na seção crítica, então algum processo (não
necessariamente o mesmo) em algum momento entra a seção crítica.
Estado ruim: varios processos esperando por condições que nunca irão ocorrer (deadlock se refere a uma condição especíca em que dois ou mais processos estão esperando porque o outro libere um recurso, ou mais de 2 processos estão esperando por recursos em uma cadeia circular);
Seção crítica
I Espera limitada (Bounded waiting) (starvation freedom?):
Deve existir um limite no número de vezes que outros processos são permitidos de entrar na seção crítica, após a requisição de um processo de entrar na seção critica e antes de que esse pedido é satisfeito (starvation (inanição): a um processo se lhe negam perpetuamente recursos necessários)
Problema da seção crítica
Como resolver o problema CS usando instruções de máquina diretamente (sem primitivas de sincronização)?
Problema enunciado para N processos, mas começamos por 2! Note: somente existem 2 estados:
ninguem na sua CS in1 = false ∧ in2 = false alguem na sua CS in1 = true ∨ in2 = true
Solução do problema da seção crítica
in1 = false; in2 = false;{¬(in1∧in2)}
Processo 1 Processo 2
while (true) { while (true) {
{¬(in1∧in2)}
in1 = true; in2 = true;
{¬(in1∧in2)}??
CS1 CS2
in1 = false; in2 = false;
NCS1 NCS2
}
Exclusão Mutua valendo sempre? Qualquer um entra na seção critica ainda que tenha outro dentro!! Sincronização!
Solução do problema da seção crítica
in1 = false; in2 = false;Processo 1 Processo 2
while (true) { while (true) {
<await not in2>; <await not in1>;
--continua quando not in1 --continua quando not in1
in1 = true; in2 = true;
{¬(in1∧in2)}??
CS1 CS2
in1 = false; in2 = false;
NCS1 NCS2
}
Exclusão Mutua valendo sempre? Não! A leitura e a atribuição precisam ser atômicas!
Solução do problema da seção crítica
in1 = false; in2 = false;Processo 1 Processo 2
while (true) { while (true) {
<await not in2; <await not in1;
in1 = true;> in2 = true;>
{¬(in1∧in2)}
CS1 CS2
in1 = false; in2 = false;
NCS1 NCS2
}
Exclusão Mutua valendo sempre? Sim! A invariante ¬(in1∧in2) é verdadeira sempre.
Não deadlock
Assumir que deadlock é possível, então ambos os processos estão bloqueados no await, ou seja,
I o processo 1 está no estado: P: {¬ in1∧in2} I e o processo 2 está no estado: Q: {in1∧¬in2}
Mas o predicado P∧Q não pode ser verdadeiro (Veja teorema de exclusão de congurações no Andrews) porque não pode acontecer ao mesmo tempo de in1 ser verdadeiro e falso e in2 ser verdadeiro e falso → Contradição! Portanto, o algoritmo é deadlock-free. Mas, como implementamos o await??
Operações atômicas comuns
I Test-and-set(TS) I Swap I Fetch-and-increment I Compare-and-swap (CAS) I Load-link/Store-conditional I Read-modify-write Exemplo:CSenter: while (TS(lock)) skip; CS
Seção crítica
Mas, será possível implementar o await usando somente registros atômicos?
Registros atômicos de leitura/escrita (read/write atomic registers): Objetos compartilhados que suportam operações de leitura e escrita atômicas.
Algoritmo 1
Cada processo anuncia que quer entrar na CS: flag1 = false; flag2 = false;
Processo 1 Processo 2
while (true) { while (true) {
flag1 = true flag2 = true
while (flag2); while (flag1);
CS1 CS2
flag1 = false; flag2 = false;
Algoritmo 1, exclusão mutua
Assumindo que os intervalos não se sobrepõem, ou seja, CSAj 9 CSBk e CSBk 9 CSAj
então:
writeA(A = true) → readA(B = false) → CSAwriteB(B = true) →
readB(A = false) → CSB
Assumindo B o último a ler o ag do outro:
writeA(A = true) → readA(B = false) → writeB(B = true) →
readB(A = false)
Chegamos a uma contradição: A==true e A==false sem nova atribuição (ela somente acontece na saida da CS e por hipótese o A ainda está na seção crítica).
Algoritmo 1, Deadlock-free?
Algoritmo 2
Cada processo deixa passar ao outro primeiro: int victim;
Processo A Processo B
while (true) { while (true) {
victim = A; --Cede a vez-- victim = B;
while (victim==A); while (victim==B);
CS1 CS2
Algoritmo 2
Provar Exclusão mutua: pelo absurdo. Assumimos: CSAj 9 CSBk e CSBk 9 CSAj
então:
writeA(victim = A) → readA(victim = B) → CSA
writeB(victim = B) → readB(victim = A) → CSB
Assumindo A o último a ler o ag do outro:
writeB(victim = B) → readB(victim = A) 9 readA(victim = B)
Chegamos a uma contradição: A não pode ter lido victim=B pois victim=A, não tem outra atribuição de victim=B até o B entrar de novo na CS e os registros são atômicos.
Algoritmo 2
Deadlock: Tem, processo A cede a vez a B, B executa e não volta a tentar entrar na CS.
Algoritmo de Peterson
Misturar o melhor de cada um. Anuncio de intenção de entrada na CS e ceder a vez para evitar deadlock.
flagA = false; flagB = false;
Processo A Processo B
while (true) { while (true) {
flagA = true; flagB = true;
victim = A; victim = B;
while(flagB && victim = A); while(flagA && victim = B);
CSA CSB
flagA = false; flagB = false;
Algoritmo de Peterson
flagA = false; flagB = false;
Processo A Processo B
while (true) { while (true) {
flagA = true; flagB = true;
victim = A; victim = B;
while(flagB && victim = A); while(flagA && victim = B);
CSA CSB
flagA = false; flagB = false;
} }
Condição do await: agB && victim = A → ca enquanto eu seja a vitima ∧ B esteja executando. Se o B já tiver tentado entrar (não sou mais a vítima) e B não esta dentro do CS posso entrar. Se o B não entrou ainda posso entrar.
A condição agB || (victim = A) exclui os casos em que agB ∧ victim=B (gera deadlock) e ¬agB ∧ victim=A valem (não entra ainda que o B não esteja executando).