Locks
Definição
Lock é um mecanismo de controle de acesso usado para garantir que apenas uma thread ou processo acesse um recurso compartilhado por vez.
Locks são essenciais para evitar condições de corrida quando múltiplas unidades de execução manipulam dados ao mesmo tempo.
Ao aplicar um lock, uma thread "tranca" o recurso, garantindo exclusividade temporária. Outras threads que tentarem acessar o mesmo recurso ficam bloqueadas até que o lock seja liberado.
Objetivos do uso de Locks
- Evitar condições de corrida em regiões críticas do código.
- Garantir consistência dos dados compartilhados entre múltiplas threads ou processos.
- Controlar o acesso sequencial a recursos como arquivos, variáveis globais ou conexões de rede.
- Sincronizar ações entre threads, impedindo conflitos de execução.
Exemplo prático
Neste exemplo, usamos threading.Lock
para proteger o acesso à variável contador
:
import threading
contador = 0
lock = threading.Lock()
def incrementar():
global contador
for _ in range(100000):
with lock:
contador += 1
t1 = threading.Thread(target=incrementar)
t2 = threading.Thread(target=incrementar)
t1.start()
t2.start()
t1.join()
t2.join()
print("Valor final:", contador)
Neste exemplo, a instrução with lock
é uma forma segura de adquirir e liberar o lock automaticamente.
Equivale a:
lock.acquire()
try:
contador += 1
finally:
lock.release()
Saída esperada
Valor final: 200000
Sem o lock, esse valor provavelmente seria menor devido a condições de corrida. Com o lock, cada incremento é feito de forma exclusiva, garantindo a integridade da variável.
O ciclo do uso de Locks
[Thread A] --- adquire lock --- executa tarefa --- libera lock
[Thread B] --- aguarda lock --- executa tarefa --- libera lock
Somente uma thread pode executar a seção protegida por vez.
As demais aguardam a liberação do lock.
Isso garante que ações críticas não ocorram simultaneamente.
Considerações importantes
Locks podem causar deadlocks se forem mal utilizados, especialmente quando múltiplos locks são adquiridos fora de ordem.
Alguns locks oferecem modos de verificação, timeout ou tentativas não bloqueantes (
tryLock
,acquire(timeout=...)
, etc.).Evite manter o lock por tempo excessivo ou dentro de blocos que podem gerar exceções inesperadas.
Para múltiplas threads coordenadas em etapas específicas, considere mecanismos adicionais como semaphores, barreiras ou condições (Condition).