(Enade, 2017) Considere o programa a seguir, que ilustra a criação, execução e sincronização de duas threads.
#include <stdio.h>
#include <pthread.h>
int x = 0, y = 0; // Variáveis compartilhadas
void funcao1(void *threadarg){
x = 1;
... // várias instruções
if (y == 0)
printf("1 ");
pthread_exit(0);
}
void funcao2(void *threadarg){
y = 1;
... // várias instruções
if (x == 0)
printf("2 ");
pthread_exit(0);
}
void main(){
pthread_t t1, t2;
// Cria e dispara t1 que executa funcao1
pthread_create(&t1, NULL,(void *)funcao1, NULL);
// Cria e dispara t2 que executa funcao2
pthread_create(&t2, NULL,(void *)funcao2, NULL);
// Pai espera filho terminar
pthread_join(t1, NULL);
// Pai espera filho terminar
pthread_join(t2, NULL);
}Ao final da execução da função main, será impresso:
A Ambos os valores "1" e "2".
B O valor "1", necessariamente.
C O valor "2", necessariamente.
D O valor "1", ou o valor "2", mas nunca ambos.
E O valor "1", ou o valor "2", ou nenhum valor, mas nunca ambos.
Para analisar as possíveis saídas do programa, precisamos considerar as diferentes formas como as instruções das duas threads (t1 e t2) podem ser intercaladas pelo escalonador do sistema operacional.
Vamos observar o que cada thread faz:
funcao1): Atribui x = 1, executa várias instruções e, em seguida, verifica se y == 0. Se for, imprime "1".funcao2): Atribui y = 1, executa várias instruções e, em seguida, verifica se x == 0. Se for, imprime "2".Agora, vamos avaliar os cenários de intercalação (assumindo o modelo de consistência sequencial, padrão em questões acadêmicas sobre o tema):
t1 executa x = 1 e depois verifica y. Como t2 ainda não começou, y ainda é 0. A condição é verdadeira e "1" é impresso.t2 inicia, executa y = 1 e verifica x. Como t1 já terminou, x vale 1. A condição é falsa e nada é impresso.t2 executa y = 1 e verifica x. Como t1 ainda não começou, x é 0. A condição é verdadeira e "2" é impresso.t1 inicia, executa x = 1 e verifica y. Como t2 já terminou, y vale 1. A condição é falsa e nada é impresso.t1 executa x = 1.t2 executa y = 1.t1 chega no if (y == 0). Como y já vale 1, a condição é falsa.t2 chega no if (x == 0). Como x já vale 1, a condição é falsa.É possível que ambos ("1" e "2") sejam impressos?
Para que "1" seja impresso, a Thread 1 precisa verificar y == 0 antes de a Thread 2 executar y = 1.
No entanto, dentro da própria Thread 1, a atribuição x = 1 ocorre antes da verificação de y.
Logo, se a Thread 1 conseguiu verificar y antes da Thread 2 alterá-lo, isso significa que a Thread 1 já teve tempo de executar x = 1. Consequentemente, quando a Thread 2 for verificar x == 0, ela encontrará x = 1 e não imprimirá "2". O raciocínio é simétrico para o caso inverso. Portanto, é logicamente impossível que ambos sejam impressos.
Conforme exposto, a resposta correta é:
E O valor "1", ou o valor "2", ou nenhum valor, mas nunca ambos.