[25]. Explique por que os spinlocks não são apropriados em sistemas monoprocessados, embora possam ser adequados em sistemas multiprocessados.
Passo 1
O enunciado pede para explicar por que os spinlocks (trancas que fazem busy-wait) não são apropriados em sistemas monoprocessados, embora possam ser adequados em sistemas multiprocessados. Vou listar o que isso envolve: (a) o que é um spinlock; (b) como ele se comporta em um sistema com vários processadores; (c) por que esse comportamento é problemático em um sistema com apenas um processador; (d) exceções e alternativas práticas.
Passo 2
O que é um spinlock — explicando devagar e sem jargão.
- Um spinlock é uma estrutura de sincronização simples: quando uma thread tenta adquirir a tranca e ela já está ocupada, a thread não dorme nem faz context switch; ela “gira” num laço (busy-wait) verificando repetidamente se a tranca foi liberada.
- Vantagem principal: a aquisição é muito rápida quando o tempo de espera é muito curto, porque evita o overhead de colocar a thread para dormir e depois acordá-la (context switch).
- Desvantagem óbvia: enquanto a thread gira, ela consome ciclos de CPU inúteis.
Passo 3
Por que spinlocks podem ser adequados em sistemas multiprocessados (SMP).
- Em SMP há vários CPUs independentes. Se uma thread em CPU 0 segura a tranca e outra thread em CPU 1 faz spin esperando, CPU 0 pode continuar rodando e liberar a tranca rapidamente; a thread em CPU 1 que está girando então pega a tranca logo em seguida.
- Comparação de custos: se o tempo esperado para a liberação da tranca for menor que o custo de um context switch (salvar estado, agendar outro processo, restaurar), é mais eficiente gastar alguns ciclos girando do que fazer uma troca de contexto.
- Logo, para seções críticas curtas e em códigos onde não se pode dormir (por exemplo em handlers de interrupção), spinlocks em SMP são sensatos.
Passo 4
O que muda em sistemas monoprocessados (UP) — o problema central.
- Em UP existe apenas um CPU. Se uma thread B está girando esperando a tranca que está com a thread A, B usa o único CPU consumindo ciclos, mas A precisa do mesmo CPU para progredir e liberar a tranca.
- Assim, B está impedindo (ou retardando) precisamente quem poderia liberar a tranca. Isso torna o spin inútil: o tempo de espera não diminui enquanto B gira — ao contrário, piora o desempenho.
- Em suma: spinning em UP desperdiça CPU que é necessário para o proprietário da tranca, enquanto bloquear a thread (fazer sleep) permitiria que o escalonador rodasse A e resolvesse o impasse mais rápido.
Passo 5
Exemplo concreto (cenário passo a passo) para fixar a ideia.
- Suponha duas threads A e B em um sistema com um único CPU.
- A entra em seção crítica e pega a tranca.
- Por algum motivo (ex.: A é preemptada ou está à espera de I/O) o escalonador para A por um tempo.
- B tenta pegar a tranca e entra em spin (busy-wait). Como há só um CPU, B fica rodando consumindo todo o tempo de CPU.
- Enquanto B gira, A não roda (ou roda muito raramente), então a liberação da tranca atrasa muito mais; performance cai muito.
- Alternativa melhor em UP: B deveria dormir (blocking mutex). Isso libera o CPU para A runificar, liberar a tranca e acordar B quando apropriado.
Passo 6
Outras desvantagens relacionadas (prioridade, consumo e previsibilidade).
- Wasted cycles: spinning consome ciclos que poderiam executar trabalho útil; em UP esse desperdício é crítico.
- Inversão de prioridade: se uma thread de alta prioridade gira esperando uma tranca que um processo de baixa prioridade segura, pode haver inversão de prioridade (e piora do comportamento real-time).
- Determinismo: em sistemas que precisam de previsibilidade temporal, spinning em UP dificulta controle temporal, pois consome a CPU sem produzir progresso.
Passo 7
Quando ainda se usa spinlocks em UP — exceções práticas.
- Em kernels uniprocessados, às vezes “spinlock” é implementado por desabilitar interrupções numa região crítica curta. Isso impede interrupções que rodariam um handler que poderia acessar a mesma estrutura. Essa técnica não é um spin no sentido clássico (não há busy-wait por outro processo), mas protege contra concorrência em contextos que não permitem sleep.
- Spinlocks puros (busy-wait) só fazem sentido em UP em casos muito específicos onde a espera é garantidamente mínima e onde dormir não é possível (por exemplo, dentro de código que não pode bloquear). Mesmo assim, desabilitar interrupções costuma ser a alternativa usada.
Passo 8
Alternativas e regra prática para provas e projetos.
- Em UP prefira locks que causem bloqueio (mutexes que colocam a thread para dormir) para esperas medianas/longas.
- Use spinlocks apenas:
- em SMP, para seções críticas muito curtas;
- quando é impossível dormir (código de interrupção, contexto atômico);
- ou quando o custo de context switch é maior que o tempo esperado de espera.
- Regra-de-ouro: se o tempo esperado de espera > custo de context switch → use blocking locks; se menor e em SMP → spinning pode ser melhor.
Passo 9
Resumo técnico e ponto final (ligando tudo).
- Em SMP, outro CPU pode progredir e liberar a tranca enquanto a thread espera, então busy-wait tem sentido para esperas curtíssimas.
- Em UP, o único CPU que poderia liberar a tranca muitas vezes fica impedido enquanto outro thread está girando, resultando em desperdício de CPU e queda de desempenho; por isso spinlocks são, em geral, inapropriados em sistemas monoprocessados.
- Portanto: spinlocks = bom em SMP para seções bem curtas ou quando não se pode dormir; em UP prefira bloquear ou usar mecanismos como desabilitar interrupções em regiões críticas muito curtas.
Resposta
Spinlocks não são apropriados em sistemas monoprocessados porque, em um único CPU, quem está fazendo spin consome os ciclos que o dono da tranca precisaria para progredir e liberar a tranca, gerando desperdício de CPU e pior desempenho; em sistemas multiprocessados outro CPU pode liberar a tranca enquanto se aguarda, tornando o busy-wait eficiente para esperas muito curtas ou quando não se pode dormir.