Pular para o conteúdo principal

Sincronização de Processos e Threads

Introdução​

Quando múltiplos threads ou processos acessam recursos compartilhados, é necessário coordenar seus acessos para evitar problemas de concorrência.

Problemas de Concorrência​

Race Condition (Condição de corrida)​

Quando o resultado depende de uma ordem de execução indeterminada.

Thread 1                 Thread 2
-------- --------
Ler contador (= 5)
Ler contador (= 5)
Incrementar (= 6)
Incrementar (= 6)
Escrever (= 6)
Escrever (= 6)

Resultado: 6 em vez de 7!

Seção Crítica​

Área de código que acessa um recurso compartilhado e deve ser executada por apenas um thread por vez.

Deadlock (Interbloqueio)​

Situação em que dois threads ficam esperando um ao outro indefinidamente.

Thread 1: Detém A, espera B
Thread 2: Detém B, espera A
-> Bloqueio permanente!

Mecanismos de Sincronização no Windows​

Visão geral​

MecanismoEscopoUtilização
Critical SectionIntra-processoAcesso rápido, mesmo processo
MutexInter-processoExclusão mútua, nomeado
SemaphoreInter-processoControle de múltiplos acessos
EventInter-processoSinalização
InterlockedIntra-processoOperações atômicas simples

Critical Section​

Conceito​

Mecanismo leve para sincronizar threads dentro de um mesmo processo.

Características​

  • Mais rápido que Mutex
  • Limitado a um único processo
  • Não nomeado

Utilização (C/C++)​

CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);

EnterCriticalSection(&cs);
// Seção crítica - apenas um thread por vez
contador++;
LeaveCriticalSection(&cs);

DeleteCriticalSection(&cs);

Em PowerShell (simulação)​

# Utilização de [System.Threading.Monitor]
$lockObj = New-Object Object
$contador = 0

[System.Threading.Monitor]::Enter($lockObj)
try {
# Seção crítica
$contador++
}
finally {
[System.Threading.Monitor]::Exit($lockObj)
}

Mutex (Exclusão Mútua)​

Conceito​

Objeto de sincronização que permite exclusão mútua entre processos.

Características​

  • Pode ser nomeado (acessível entre processos)
  • Mais lento que Critical Section
  • Possível detectar deadlock

Utilização em PowerShell​

# Criar ou abrir um mutex nomeado
$mutexName = "Global\MonMutex"
$mutex = New-Object System.Threading.Mutex($false, $mutexName)

try {
# Esperar pelo mutex (timeout 5 segundos)
if ($mutex.WaitOne(5000)) {
try {
Write-Host "Mutex adquirido - seção crítica"
# Trabalho exclusivo aqui
Start-Sleep -Seconds 2
}
finally {
$mutex.ReleaseMutex()
Write-Host "Mutex liberado"
}
}
else {
Write-Host "Timeout - mutex não adquirido"
}
}
finally {
$mutex.Close()
}

Mutex nomeado vs anônimo​

TipoNomeEscopo
AnônimoNenhumThreads do mesmo processo
Local"Local\Nome"Sessão do usuário
Global"Global\Nome"Todos os processos do sistema

Semaphore​

Conceito​

Permite limitar o número de acessos simultâneos a um recurso.

Diferença em relação ao Mutex​

MutexSemaphore
1 único acessoN acessos simultâneos
Proprietário únicoSem proprietário

Utilização em PowerShell​

# Semaphore com 3 vagas
$semName = "Global\MonSemaphore"
$semaphore = New-Object System.Threading.Semaphore(3, 3, $semName)

try {
if ($semaphore.WaitOne(5000)) {
try {
Write-Host "Vaga adquirida no semaphore"
# Trabalho limitado a 3 threads simultâneos
Start-Sleep -Seconds 2
}
finally {
$semaphore.Release()
Write-Host "Vaga liberada"
}
}
}
finally {
$semaphore.Close()
}

Casos de uso​

  • Pool de conexões de banco de dados
  • Limitação de downloads simultâneos
  • Controle de recursos limitados

Event​

Conceito​

Permite que um thread sinalize um evento para outros threads.

Tipos de events​

TipoComportamento
AutoResetLibera um único thread e volta ao estado não sinalizado
ManualResetPermanece sinalizado até reset explícito

Utilização em PowerShell​

# Event ManualReset
$eventName = "Global\MonEvent"
$event = New-Object System.Threading.EventWaitHandle(
$false, # Estado inicial: não sinalizado
[System.Threading.EventResetMode]::ManualReset,
$eventName
)

# No thread produtor
$event.Set() # Sinalizar
$event.Reset() # Voltar para não sinalizado

# No thread consumidor
if ($event.WaitOne(5000)) {
Write-Host "Evento recebido!"
}

$event.Close()

Interlocked Operations​

Conceito​

Operações atômicas em variáveis, sem necessidade de lock explícito.

Operações disponíveis​

OperaçãoDescrição
IncrementIncrementa em 1
DecrementDecrementa em 1
AddAdiciona um valor
ExchangeTroca valores
CompareExchangeTroca condicional

Utilização em PowerShell​

# Incremento atômico
$contador = 0
[System.Threading.Interlocked]::Increment([ref]$contador)
Write-Host "Contador: $contador"

# Add atômico
[System.Threading.Interlocked]::Add([ref]$contador, 10)
Write-Host "Contador após add: $contador"

# Exchange atômico
$oldValue = [System.Threading.Interlocked]::Exchange([ref]$contador, 100)
Write-Host "Valor antigo: $oldValue, Novo: $contador"

Boas Práticas​

Evitar Deadlocks​

  1. Ordem constante: Sempre adquirir os locks na mesma ordem
  2. Timeouts: Usar timeouts para detectar bloqueios
  3. Minimizar: Manter as seções críticas curtas

Exemplo de deadlock evitado​

# RUIM - risco de deadlock
# Thread 1: Lock A depois Lock B
# Thread 2: Lock B depois Lock A

# BOM - ordem constante
# Thread 1: Lock A depois Lock B
# Thread 2: Lock A depois Lock B (mesma ordem!)

Performance​

MecanismoPerformanceCaso de uso
InterlockedMuito rápidoOperações simples
Critical SectionRápidoMesmo processo
MutexMédioInter-processo
SemaphoreMédioAcesso múltiplo limitado

Detecção de Problemas​

Process Explorer​

  • View > Show Lower Pane
  • Handle view: ver os handles de sincronização

WinDbg​

!locks          # Exibir locks
!handle # Exibir handles
!analyze -hang # Analisar um hang/deadlock

Performance Monitor​

Contadores úteis:

  • Process > Handle Count
  • Thread > Context Switches/sec

Exercícios​

  1. Crie um script com dois jobs PowerShell que usam um mutex compartilhado
  2. Implemente um contador thread-safe com Interlocked
  3. Crie um semaphore que limite a 2 execuções simultâneas

Referências​